6 Commits

43 changed files with 1765 additions and 1811 deletions

View File

View File

View File

@@ -6,7 +6,6 @@ from model.Log import LogHelper
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():

View File

@@ -7,12 +7,12 @@ from model.Block import Block
from model.Utilities import HtmlHelper
block_bp = Blueprint('block', __name__)
block = Block()
# --- Add Block page -------
@block_bp.route('/add_block', methods=['GET', 'POST'])
@login_required
def add_block():
block = Block()
# block = Block()
if request.method == 'POST':
block.AddBlock(request)
@@ -62,7 +62,7 @@ def get_districts(state_id):
@login_required
def check_block():
block = Block()
# block = Block()
return block.CheckBlock(request)
@@ -70,8 +70,8 @@ def check_block():
@login_required
def edit_block(block_id):
block = Block()
# block = Block()
if request.method == 'POST':
block.EditBlock(request, block_id)
block.resultMessage
@@ -90,7 +90,10 @@ def edit_block(block_id):
for rs in cursor.stored_results():
states = rs.fetchall()
cursor.callproc("GetAllDistrictsData")
# cursor.callproc("GetAllDistrictsData")
# for rs in cursor.stored_results():
# districts = rs.fetchall()
cursor.callproc("GetAllDistricts")
for rs in cursor.stored_results():
districts = rs.fetchall()
@@ -111,7 +114,7 @@ def edit_block(block_id):
@login_required
def delete_block(block_id):
block = Block()
# block = Block()
block.DeleteBlock(request, block_id)
return redirect(url_for('block.add_block'))

View File

@@ -5,12 +5,13 @@ from model.District import District
from model.State import State
district_bp = Blueprint('district', __name__)
district = District()
# ------- District page --------
@district_bp.route('/add_district', methods=['GET', 'POST'])
@login_required
def add_district():
district = District()
# district = District()
if request.method == 'POST':
district.AddDistrict(request=request)
@@ -32,7 +33,7 @@ def add_district():
@login_required
def check_district():
district = District()
# district = District()
return district.CheckDistrict(request=request)
@@ -41,7 +42,7 @@ def check_district():
@login_required
def delete_district(district_id):
district = District()
# district = District()
district.DeleteDistrict(request=request, district_id=district_id)
@@ -56,7 +57,7 @@ def delete_district(district_id):
@login_required
def edit_district(district_id):
district = District()
# district = District()
state = State()
if request.method == 'POST':

View File

@@ -59,21 +59,21 @@ def show_table(filename):
try:
cursor = connection.cursor(dictionary=True)
cursor.callproc('GetStateByName', [file_info['State']])
cursor.callproc('CheckStateExists', [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.")
if state_data:
cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']])
cursor.callproc('GetDistrictByNameAndState', [file_info['District'], state_data['State_Id']]) # Change GetDistrictByNameAndStates to GetDistrictByNameAndState
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']}'.")
if district_data:
cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']])
cursor.callproc('GetBlockByNameAndDistrict', [file_info['Block'], district_data['District_id']]) #Change District_ID to District_id and GetBlockByNameAndDistricts to GetBlockByNameAndDistrict
for result in cursor.stored_results():
block_data = result.fetchone()
if not block_data:
@@ -84,7 +84,9 @@ def show_table(filename):
subcontractor_data = result.fetchone()
if not subcontractor_data:
cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']])
# cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']])
# connection.commit()
cursor.callproc('SaveContractor', [file_info.get('Subcontractor'),None,None,None,None,None,None,None,None])
connection.commit()
cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
for result in cursor.stored_results():
@@ -229,7 +231,7 @@ def save_data():
else:
work_type = " ".join(words[:work_pos + 1])
if Invoice_Details and 'village' in Invoice_Details.lower() and 'work' in Invoice_Details.lower():
print("village_name ::", village_name, "|| work_type ::", work_type)
# print("village_name ::", village_name, "|| work_type ::", work_type)
if block_id and village_name:
village_id = None
cursor.callproc("GetVillageId", (block_id, village_name))
@@ -242,11 +244,11 @@ def save_data():
for result in cursor.stored_results():
result = result.fetchone()
village_id = result[0] if result else None
print("village_id :", village_id)
print("block_id :", block_id)
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)
# print("village_id :", village_id)
# print("block_id :", block_id)
# 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)
args = (
PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No,
@@ -255,22 +257,39 @@ def save_data():
subcontractor_id, 0
)
print("All invoice Details ",args)
# print("All invoice Details ",args)
# add subcontarctor id in invoice table
results = cursor.callproc('SaveInvoice', args)
invoice_id = results[-1]
print("invoice id from the excel ", invoice_id)
print("**************************************************************")
print(invoice_id)
print("**************************************************************")
cursor.callproc(
"SavePayment",
(
PMC_No,
Invoice_No, # required
Payment_Amount,
TDS_Payment_Amount,
Total_Amount,
UTR,
invoice_id # last
)
)
# print("invoice id from the excel ", invoice_id)
if isinstance(hold_columns, str):
hold_columns = ast.literal_eval(hold_columns)
if isinstance(hold_columns, list) and all(isinstance(hold, dict) for hold in hold_columns):
for hold in hold_columns:
print(f"Processing hold: {hold}")
# print(f"Processing hold: {hold}")
hold_column_name = hold.get('column_name') # Get column name
hold_type_id = hold.get('hold_type_id') # Get hold_type_id
if hold_column_name:
hold_amount = entry.get(
hold_column_name) # Get the value for that specific hold column
if hold_amount is not None:
print(f"Processing hold type: {hold_column_name}, Hold Amount: {hold_amount}")
# print(f"Processing hold type: {hold_column_name}, Hold Amount: {hold_amount}")
hold_join_data = {
"Contractor_Id": subcontractor_id,
"Invoice_Id": invoice_id,
@@ -288,9 +307,9 @@ def save_data():
else:
print("Hold columns data is not a valid list of dictionaries.")
#---------------------------------------------Credit Note---------------------------------------------------------------------------
elif any(keyword in Invoice_Details.lower() for keyword in ['credit note','logging report']):
print("Credit note found:", PMC_No, Invoice_No, Basic_Amount, Debit_Amount, Final_Amount,
After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No)
elif any(keyword in Invoice_Details.lower() for keyword in ['credit note','logging report','credit','Credit note Details']):
# print("Credit note found:", PMC_No, Invoice_No, Basic_Amount, Debit_Amount, Final_Amount,
# After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No)
cursor.callproc(
'AddCreditNoteFromExcel',
[
@@ -311,56 +330,113 @@ def save_data():
'excess hold amount',
'Multi to Single layer bill',
'hold amount',
'logging report'
'logging report',
'hold'
]
# Step 3: Matching condition
if any(kw in normalized_details for kw in keywords):
print("✅ Match found. Inserting hold release for:", Invoice_Details)
# print("✅ Match found. Inserting hold release for:", Invoice_Details)
cursor.callproc(
'AddHoldReleaseFromExcel',
[PMC_No, Invoice_No, Invoice_Details, Basic_Amount, Final_Amount, UTR, subcontractor_id]
)
connection.commit()
print("✅ Hold release inserted for:", PMC_No, Invoice_Details)
# print("✅ Hold release inserted for:", PMC_No, Invoice_Details)
#------------------------------------------------------------------------------------------------------------------
elif Invoice_Details and any(
keyword in Invoice_Details.lower() for keyword in ['gst', 'release', 'note']):
print("Gst rels :", PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR, subcontractor_id)
keyword in Invoice_Details.lower() for keyword in ['gst', 'release', 'gst release note']):
# print("Gst rels :", PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR, subcontractor_id)
cursor.callproc(
'AddGSTReleaseFromExcel',
[PMC_No, Invoice_No, Basic_Amount, Final_Amount, Total_Amount, UTR, subcontractor_id]
)
if PMC_No and Total_Amount and UTR:
print("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 ))
if not village_id:
village_id = None
cursor.callproc('InsertOrUpdateInPayment', (
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,
0,
GST_SD_Amount,
Final_Amount,
Payment_Amount,
TDS_Payment_Amount,
Total_Amount,
UTR,
subcontractor_id
))
# --------------------------------------
# If no village/work detected, only PMC/Payment
if not (Invoice_Details and 'village' in Invoice_Details.lower() and 'work' in Invoice_Details.lower()):
# if not (Invoice_Details and 'village' in Invoice_Details.lower() and 'work' in Invoice_Details.lower() and 'gst' in Invoice_Details.lower() and 'gst release note' in Invoice_Details.lower() and 'release' in Invoice_Details.lower()):
# ---------- Only PMC / Payment rows ----------
if PMC_No and not Invoice_No and UTR :
# print("No village/work, using PMC only :", PMC_No)
# check invoice exists
# cursor.execute(
# "SELECT invoice_id FROM invoice WHERE PMC_No=%s ORDER BY invoice_id DESC LIMIT 1",
# (PMC_No,)
# )
# row = cursor.fetchone()
# invoice_id = row[0] if row else None
# # insert invoice if not exists
# if not invoice_id:
print(" extra payment :", PMC_No,Total_Amount,UTR, subcontractor_id)
# cursor.execute(
# """
# INSERT INTO invoice (PMC_No,Contractor_Id) VALUES (%s, %s);
# """,
# (PMC_No, subcontractor_id)
# )
# connection.commit()
# cursor.execute(
# "SELECT invoice_id FROM invoice WHERE PMC_No=%s AND Contractor_Id =%s ORDER BY invoice_id DESC LIMIT 1",
# (PMC_No, subcontractor_id)
# )
# row = cursor.fetchone()
cursor.callproc("insertExtrapaymet",(PMC_No, subcontractor_id))
for result in cursor.stored_results():
row = result.fetchone()
invoice_id = row[0] if row else None
# insert payment
cursor.callproc(
"SavePayment",
(
PMC_No,
Invoice_No, # required
Payment_Amount,
TDS_Payment_Amount,
Total_Amount,
UTR,
invoice_id
)
)
# if PMC_No and Total_Amount and UTR:
# print("Payment :", PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR )
# Add inoice id in payment table
# cursor.callproc("SavePayment",(PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR, invoice_id))
# if not village_id:
# village_id = None
# cursor.callproc('InsertOrUpdateInPayment', (
# 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,
# 0,
# GST_SD_Amount,
# Final_Amount,
# Payment_Amount,
# TDS_Payment_Amount,
# Total_Amount,
# UTR,f
# subcontractor_id
# ))
connection.commit()
return jsonify({"success": "Data saved successfully!"}), 200
except Exception as e:

View File

@@ -5,12 +5,12 @@ from model.GST import GST
hold_bp = Blueprint("hold_types", __name__)
hold = HoldTypes()
# ---------------- ADD HOLD TYPE ----------------
@hold_bp.route('/add_hold_type', methods=['GET','POST'])
@login_required
def add_hold_type():
hold = HoldTypes()
# hold = HoldTypes()
if request.method == 'POST':
hold.AddHoldType(request)
@@ -30,7 +30,7 @@ def add_hold_type():
@login_required
def check_hold_type():
hold = HoldTypes()
# hold = HoldTypes()
return hold.CheckHoldType(request) # if exists
@@ -39,7 +39,7 @@ def check_hold_type():
@login_required
def edit_hold_type(id):
hold = HoldTypes()
# hold = HoldTypes()
if request.method == 'POST':
hold.EditHoldType(request, id) # ✅
@@ -58,7 +58,7 @@ def edit_hold_type(id):
@login_required
def delete_hold_type(id):
hold = HoldTypes()
# hold = HoldTypes()
hold.DeleteHoldType(request, id) # ✅
return redirect(url_for("hold_types.add_hold_type"))

View File

@@ -1,6 +1,3 @@
# controllers/invoice_controller.py
from flask import Blueprint, request, jsonify, render_template
@@ -40,7 +37,11 @@ def add_invoice():
village_id = village_result['Village_Id']
invoice_id = insert_invoice(data, village_id)
assign_subcontractor(data, village_id)
# assign_subcontractor(data, village_id)
print("********************************************************************")
print("Manully added invoice id :",invoice_id)
print("********************************************************************")
insert_hold_types(data, invoice_id)
log_action("Add invoice", f"added invoice '{data.get('pmc_no')}'")
@@ -83,7 +84,7 @@ def edit_invoice(invoice_id):
if request.method == 'POST':
data = request.form
update_invoice(data, invoice_id)
update_inpayment(data)
# update_inpayment(data)
log_action("Edit invoice", f"edited invoice '{invoice_id}'")
return jsonify({"status": "success", "message": "Invoice updated successfully"}), 200

View File

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

View File

@@ -28,8 +28,8 @@ def add_payment():
utr = request.form['utr']
LogHelper.log_action("Add Payment", f"User {current_user.id} Add Payment '{pmc_no}'")
Paymentmodel.insert_payment(pmc_no, invoice_no, amount, tds_amount, total_amount, utr)
Paymentmodel.update_inpayment(subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr)
Paymentmodel.insert_payment(subcontractor_id,pmc_no, invoice_no, amount, tds_amount, total_amount, utr)
# Paymentmodel.update_inpayment(subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr)
return redirect(url_for('payment_bp.add_payment'))
@@ -72,15 +72,13 @@ def edit_payment(payment_id):
Paymentmodel.call_update_payment_proc(payment_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr)
# Update inpayment
connection = Paymentmodel.get_connection()
cursor = connection.cursor()
cursor.callproc(
'UpdateInpaymentByPMCInvoiceUTR',
[amount, tds_amount, total_amount, pmc_no, invoice_no, utr]
)
connection.commit()
cursor.close()
connection.close()
# connection = Paymentmodel.get_connection()
# cursor = connection.cursor()
# cursor.callproc('UpdateInpaymentByPMCInvoiceUTR',[amount, tds_amount, total_amount, pmc_no, invoice_no, utr])
# connection.commit()
# cursor.close()
# connection.close()
return redirect(url_for('payment_bp.add_payment'))

View File

@@ -1,40 +1,40 @@
from flask import Blueprint, render_template, send_from_directory
from flask_login import login_required
# from flask import Blueprint, render_template, send_from_directory
# from flask_login import login_required
# from model.PmcReport import PmcReport
from model.PmcReport import PmcReport
# pmc_report_bp = Blueprint("pmc_report", __name__)
pmc_report_bp = Blueprint("pmc_report", __name__)
# # ---------------- Contractor Report by pmc no ----------------
# @pmc_report_bp.route("/pmc_report/<pmc_no>")
# @login_required
# def pmc_report(pmc_no):
# data = PmcReport.get_pmc_report(pmc_no)
# if not data:
# return "No PMC found with this number", 404
# ---------------- Contractor Report by pmc no ----------------
@pmc_report_bp.route("/pmc_report/<pmc_no>")
@login_required
def pmc_report(pmc_no):
data = PmcReport.get_pmc_report(pmc_no)
if not data:
return "No PMC found with this number", 404
# return render_template(
# "pmc_report.html",
# info=data["info"],
# invoices=data["invoices"],
# hold_types=data["hold_types"],
# gst_rel=data["gst_rel"],
# payments=data["payments"],
# credit_note=data["credit_note"],
# hold_release=data["hold_release"],
# total=data["total"]
# )
return render_template(
"pmc_report.html",
info=data["info"],
invoices=data["invoices"],
hold_types=data["hold_types"],
gst_rel=data["gst_rel"],
payments=data["payments"],
credit_note=data["credit_note"],
hold_release=data["hold_release"],
total=data["total"]
)
# # ---------------- Contractor Download Report by pmc no ----------------
# @pmc_report_bp.route("/download_pmc_report/<pmc_no>")
# @login_required
# def download_pmc_report(pmc_no):
# ---------------- Contractor Download Report by pmc no ----------------
@pmc_report_bp.route("/download_pmc_report/<pmc_no>")
@login_required
def download_pmc_report(pmc_no):
# result = PmcReport.download_pmc_report(pmc_no)
result = PmcReport.download_pmc_report(pmc_no)
# if not result:
# return "No contractor found for this PMC No", 404
if not result:
return "No contractor found for this PMC No", 404
# output_folder, file_name = result
output_folder, file_name = result
# return send_from_directory(output_folder, file_name, as_attachment=True)
return send_from_directory(output_folder, file_name, as_attachment=True)

View File

@@ -1,13 +1,10 @@
from flask import Blueprint, render_template, request, jsonify
from flask import Blueprint, render_template, request, jsonify, send_file
from flask_login import login_required, current_user
from services.ReportService import ReportService
from model.Report import ReportHelper
from model.Log import LogHelper
report_bp = Blueprint("report", __name__)
# ---------------- Report Page ----------------
@report_bp.route("/report")
@login_required
@@ -20,28 +17,45 @@ def report_page():
@login_required
def search_contractor():
subcontractor_name = request.form.get("subcontractor_name")
LogHelper.log_action(
"Search Contractor",
f"User {current_user.id} searched contractor '{subcontractor_name}'"
)
data = ReportHelper.search_contractor(request)
return jsonify(data)
# Pagination (basic)
page = int(request.form.get("page", 1))
per_page = 20
start = (page - 1) * per_page
end = start + per_page
paginated_data = data[start:end]
return jsonify({
"data": paginated_data,
"total": len(data)
})
# ---------------- Contractor Report by contractor id ----------------
@report_bp.route('/contractor_report/<int:contractor_id>')
@login_required
def contractor_report(contractor_id):
data = ReportHelper.get_contractor_report(contractor_id)
service = ReportService(contractor_id=contractor_id).load_data()
return render_template(
'subcontractor_report.html',
contractor_id=contractor_id,
**data
**service.get_web_data()
)
# ---------------- Contractor Report by pmc no ----------------
@report_bp.route("/pmc_report/<pmc_no>")
@login_required
def pmc_report(pmc_no):
service = ReportService(pmc_no=pmc_no).load_data()
return render_template(
"pmc_report.html",
**service.get_web_data()
)
# ---------------- Contractor Download Report by contractor id ----------------
@@ -49,5 +63,24 @@ def contractor_report(contractor_id):
@login_required
def download_report(contractor_id):
return ReportHelper().download_report(contractor_id=contractor_id)
service = ReportService(contractor_id=contractor_id).load_data()
file, error = service.download_excel()
if error:
return error, 404
return send_file(file, as_attachment=True)
# ---------------- Contractor Download Report by pmc no ----------------
@report_bp.route("/download_pmc_report/<pmc_no>")
@login_required
def download_pmc_report(pmc_no):
service = ReportService(pmc_no=pmc_no).load_data()
file, error = service.download_excel()
if error:
return error, 404
return send_file(file, as_attachment=True)

View File

@@ -3,13 +3,13 @@ from flask_login import login_required
from model.State import State
state_bp = Blueprint('state', __name__)
state = State()
# ----- State page ------
@state_bp.route('/add_state', methods=['GET', 'POST'])
@login_required
def add_state():
state = State()
# state = State()
if request.method == 'POST':
state.AddState(request=request)
@@ -24,7 +24,7 @@ def add_state():
@login_required
def check_state():
state = State()
# state = State()
return state.CheckState(request=request)
@@ -33,7 +33,7 @@ def check_state():
@login_required
def deleteState(id):
state = State()
# state = State()
state.DeleteState(request=request, id=id)
@@ -47,7 +47,7 @@ def deleteState(id):
@login_required
def editState(id):
state = State()
# state = State()
if request.method == 'POST':

View File

@@ -11,14 +11,14 @@ from model.State import State
# Create Blueprint
village_bp = Blueprint('village', __name__)
village = Village()
# ------------------------- Add Village -------------------------
@village_bp.route('/add_village', methods=['GET', 'POST'])
@login_required
def add_village():
village = Village()
# village = Village()
if request.method == 'POST':
village.AddVillage(request=request)
@@ -79,14 +79,14 @@ def get_blocks(district_id):
@village_bp.route('/check_village', methods=['POST'])
@login_required
def check_village():
village = Village()
# village = Village()
return village.CheckVillage(request=request)
@village_bp.route('/delete_village/<int:village_id>')
@login_required
def delete_village(village_id):
village = Village()
# village = Village()
village.DeleteVillage(request=request, village_id=village_id)
# ✅ Convert resultMessage to string if it's a Response or tuple
@@ -112,7 +112,7 @@ def delete_village(village_id):
@login_required
def edit_village(village_id):
village = Village()
# village = Village()
if request.method == 'POST':

View File

@@ -16,7 +16,7 @@ from controllers.payment_controller import payment_bp
from controllers.gst_release_controller import gst_release_bp
from controllers.excel_upload_controller import excel_bp
from controllers.report_controller import report_bp
from controllers.pmc_report_controller import pmc_report_bp
# from controllers.pmc_report_controller import pmc_report_bp
from controllers.hold_types_controller import hold_bp
from dotenv import load_dotenv
@@ -57,7 +57,7 @@ app.register_blueprint(payment_bp)
app.register_blueprint(gst_release_bp)
app.register_blueprint(excel_bp)
app.register_blueprint(report_bp)
app.register_blueprint(pmc_report_bp)
# app.register_blueprint(pmc_report_bp)
app.register_blueprint(hold_bp)
# ---------------- Run App ----------------

View File

@@ -1,16 +1,6 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper
import os
import config
import re
import mysql.connector
from mysql.connector import Error
from model.ItemCRUD import ItemCRUD, itemCRUDMapping
from model.ItemCRUD import ItemCRUD
class Block:
@@ -22,9 +12,7 @@ class Block:
self.isSuccess = False
self.resultMessage = ""
# ----------------------------------------------------------
# Add Block
# ----------------------------------------------------------
def AddBlock(self, request):
block = ItemCRUD(itemType=ItemCRUDType.Block)
@@ -37,17 +25,7 @@ class Block:
self.resultMessage = block.resultMessage
return
# ----------------------------------------------------------
# Get All Blocks
# ----------------------------------------------------------
# def GetAllBlocks(self):
# block = ItemCRUD(itemType=ItemCRUDType.Block)
# blocksdata = block.GetAllData(request=request, storedproc="GetAllBlock")
# self.isSuccess = block.isSuccess
# self.resultMessage = block.resultMessage
# return blocksdata
# Get All Blocks
def GetAllBlocks(self, request):
block = ItemCRUD(itemType=ItemCRUDType.Block)
@@ -57,18 +35,8 @@ class Block:
self.resultMessage = block.resultMessage
return blocksdata
# ----------------------------------------------------------
# Check Block Exists
# ----------------------------------------------------------
# def CheckBlock(self, request):
# block = ItemCRUD(itemType=ItemCRUDType.Block)
# block_name = request.json.get('block_Name', '').strip()
# district_id = request.json.get('district_Id')
# result = block.CheckItem(request=request, parentid=district_id, childname=block_name, storedprocfetch="GetBlockByNameAndDistrict")
# self.isSuccess = block.isSuccess
# self.resultMessage = block.resultMessage
# return result
def CheckBlock(self, request):
block = ItemCRUD(itemType=ItemCRUDType.Block)
data = request.get_json(silent=True) or request.form
@@ -85,24 +53,7 @@ class Block:
self.resultMessage = block.resultMessage
return result
# ----------------------------------------------------------
# Get Block By ID
# ----------------------------------------------------------
# def GetBlockByID(self, id):
# block = ItemCRUD(itemType=ItemCRUDType.Village)
# blockdata = block.GetAllData(id=id,storedproc="GetBlockDataByID")
# self.isSuccess = block.isSuccess
# self.resultMessage = block.resultMessage
# print("akash"+blockdata)
# return blockdata
# def GetBlockByID(self,request,id):
# block = ItemCRUD(itemType=ItemCRUDType.Block)
# blockdata = block.GetDataByID(request=request,id=id,storedproc="GetBlockDataByID")
# self.isSuccess = block.isSuccess
# self.resultMessage = block.resultMessage
# return blockdata
def GetBlockByID(self, id):
block = ItemCRUD(itemType=ItemCRUDType.Block)
@@ -116,20 +67,8 @@ class Block:
self.resultMessage = block.resultMessage
return blockdata
# ----------------------------------------------------------
# Update Block
# ----------------------------------------------------------
# def EditBlock(self, request, block_id):
# block = ItemCRUD(itemType=ItemCRUDType.Block)
# district_id = request.form.get('district_Id')
# block_name = request.form.get('block_Name', '').strip()
# block.EditItem(request=request, childid=block_id, parentid=district_id, childname=block_name, storedprocadd="UpdateBlockById" )
# self.isSuccess = block.isSuccess
# self.resultMessage = block.resultMessage
# return
def EditBlock(self, request, block_id):
block = ItemCRUD(itemType=ItemCRUDType.Block)
@@ -149,9 +88,8 @@ class Block:
self.resultMessage = block.resultMessage
return
# ----------------------------------------------------------
# Delete Block
# ---------------------------------------------------------
# Delete Block
def DeleteBlock(self,request, id):
block = ItemCRUD(itemType=ItemCRUDType.Block)

View File

@@ -1,10 +1,5 @@
from mysql.connector import Error
import config
from datetime import datetime
class ContractorInfo:
def __init__(self, contractor_id):

View File

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

View File

@@ -3,7 +3,6 @@ from model.ItemCRUD import ItemCRUD
from model.Utilities import ItemCRUDType
class HoldTypes:
"""CRUD operations for Hold Types using ItemCRUD"""
def __init__(self):
self.isSuccess = False
@@ -33,7 +32,7 @@ class HoldTypes:
self.isSuccess = hold.isSuccess
self.resultMessage = hold.resultMessage
# Convert tuple → dictionary
# Convert tuple → dictionary
data = []
for row in rows:
data.append({
@@ -51,7 +50,7 @@ class HoldTypes:
self.isSuccess = hold.isSuccess
self.resultMessage = hold.resultMessage
# Convert tuple → dictionary
# Convert tuple → dictionary
if row:
return {
"hold_type_id": row[0],

View File

@@ -71,22 +71,22 @@ def get_all_villages():
def insert_invoice(data, village_id):
def operation(cursor):
# Insert invoice
cursor.callproc('InsertInvoice', [
data.get('pmc_no'),
village_id,
data.get('work_type'),
data.get('invoice_details'),
data.get('invoice_date'),
data.get('invoice_no'),
*get_numeric_values(data)
])
invoice_row = fetch_one(cursor)
if not invoice_row:
raise Exception("Invoice ID not returned")
invoice_id = invoice_row.get('invoice_id')
# cursor.callproc('InsertInvoice', [
# data.get('pmc_no'),
# village_id,
# data.get('work_type'),
# data.get('invoice_details'),
# data.get('invoice_date'),
# data.get('invoice_no'),
# *get_numeric_values(data),
# data.get('subcontractor_id')
# Insert inpayment
cursor.callproc('InsertInpayment', [
# ])
# invoice_row = fetch_one(cursor)
# if not invoice_row:
# raise Exception("Invoice ID not returned")
# invoice_id = invoice_row.get('invoice_id')
cursor.callproc('SaveInvoice', [
data.get('pmc_no'),
village_id,
data.get('work_type'),
@@ -94,9 +94,27 @@ def insert_invoice(data, village_id):
data.get('invoice_date'),
data.get('invoice_no'),
*get_numeric_values(data),
data.get('subcontractor_id')
data.get('subcontractor_id'),
0
])
clear_results(cursor)
invoice_id = None
for result in cursor.stored_results():
row = result.fetchone()
if row:
invoice_id = row['invoice_id']
# # Insert inpayment
# cursor.callproc('InsertInpayment', [
# data.get('pmc_no'),
# village_id,
# data.get('work_type'),
# data.get('invoice_details'),
# data.get('invoice_date'),
# data.get('invoice_no'),
# *get_numeric_values(data),
# data.get('subcontractor_id')
# ])
# clear_results(cursor)
return invoice_id
return execute_db_operation(operation)
@@ -141,18 +159,18 @@ def update_invoice(data, invoice_id):
clear_results(cursor)
execute_db_operation(operation)
def update_inpayment(data):
def operation(cursor):
cursor.callproc('UpdateInpayment', [
data.get('work_type'),
data.get('invoice_details'),
data.get('invoice_date'),
*get_numeric_values(data),
data.get('pmc_no'),
data.get('invoice_no')
])
clear_results(cursor)
execute_db_operation(operation)
# def update_inpayment(data):
# def operation(cursor):
# cursor.callproc('UpdateInpayment', [
# data.get('work_type'),
# data.get('invoice_details'),
# data.get('invoice_date'),
# *get_numeric_values(data),
# data.get('pmc_no'),
# data.get('invoice_no')
# ])
# clear_results(cursor)
# execute_db_operation(operation)
def delete_invoice_data(invoice_id, user_id):
def operation(cursor):
@@ -173,22 +191,22 @@ def delete_invoice_data(invoice_id, user_id):
clear_results(cursor)
# Delete inpayment
cursor.callproc('DeleteInpaymentByPMCInvoice', (pmc_no, invoice_no))
clear_results(cursor)
# cursor.callproc('DeleteInpaymentByPMCInvoice', (pmc_no, invoice_no))
# clear_results(cursor)
execute_db_operation(operation)
# ------------------- Subcontractor Functions -------------------
def assign_subcontractor(data, village_id):
def operation(cursor):
cursor.callproc('AssignSubcontractor', [
data.get('pmc_no'),
data.get('subcontractor_id'),
village_id
])
clear_results(cursor)
execute_db_operation(operation)
# def assign_subcontractor(data, village_id):
# def operation(cursor):
# cursor.callproc('AssignSubcontractor', [
# data.get('pmc_no'),
# data.get('subcontractor_id'),
# village_id
# ])
# clear_results(cursor)
# execute_db_operation(operation)
# ------------------- Hold Types Functions -------------------

View File

@@ -232,14 +232,13 @@ class ItemCRUD:
if self.itemCRUDType.name == "GSTRelease" and data:
cursor.callproc(storedprocupdate, (
childid,
data['PMC_No'],
data['Invoice_No'],
data['Basic_Amount'],
data['Final_Amount'],
data['Total_Amount'],
data['UTR'],
data['Contractor_ID']
data['p_pmc_no'], # PMC_No
data['p_invoice_no'], # Invoice_No
data['p_basic_amount'], # Basic_Amount
data['p_final_amount'], # Final_Amount
data['p_total_amount'], # Total_Amount
data['p_utr'], # UTR
data['p_gst_release_id']# GST_Release_Id
))
connection.commit()

View File

@@ -1,29 +1,40 @@
import os
from datetime import datetime
from flask import current_app
from flask_login import current_user
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from datetime import datetime
from model.FolderAndFile import FolderAndFile
class LogHelper:
@staticmethod
def log_action(action, details=""):
"""Add a log entry."""
log_data = LogData()
log_data.add_log(action, details)
"""Log user actions with timestamp, user, action, and details."""
logData = LogData()
logData.WriteLog(action, details="")
class LogData:
filepath = ""
timestamp = None
def __init__(self):
self.filepath = os.path.join(current_app.root_path, 'activity.log')
self.filepath = FolderAndFile.get_activity_log_path('activity.log')
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.user = getattr(current_user, "cn", None) \
or getattr(current_user, "username", None) \
or getattr(current_user, "sAMAccountName", "Unknown")
self.user = LogData.get_current_user()
def add_log(self, action, details=""):
"""Create/Add a log entry."""
@staticmethod
def get_current_user():
if hasattr(current_user, "cn") and current_user.cn:
return current_user.cn
elif hasattr(current_user, "username") and current_user.username:
return current_user.username
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
return current_user.sAMAccountName
return "Unknown"
def WriteLog(self, action, details=""):
"""Log user actions with timestamp, user, action, and details."""
with open(self.filepath, "a", encoding="utf-8") as f:
f.write(
f"Timestamp: {self.timestamp} | "
@@ -32,73 +43,41 @@ class LogData:
f"Details: {details}\n"
)
def get_all_logs(self):
"""Read all logs."""
def GetActivitiesLog(self):
logs = []
if os.path.exists(self.filepath):
with open(self.filepath, 'r', encoding="utf-8") as f:
with open(self.filepath, 'r') as f:
for line in f:
parts = line.strip().split(" | ")
if len(parts) == 4:
logs.append({
"timestamp": parts[0].split(":", 1)[1].strip(),
"user": parts[1].split(":", 1)[1].strip(),
"action": parts[2].split(":", 1)[1].strip(),
"details": parts[3].split(":", 1)[1].strip()
"timestamp": parts[0].replace("Timestamp:", "").strip(),
"user": parts[1].replace("User:", "").strip(),
"action": parts[2].replace("Action:", "").strip(),
"details": parts[3].replace("Details:", "").strip()
})
return logs
def get_filtered_logs(self, start_date=None, end_date=None, user_name=None):
"""Filter logs by date and/or user."""
logs = self.get_all_logs()
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
filtered_logs = self.GetActivitiesLog()
# Filter by date
if start_date or end_date:
start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min
end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max
logs = [
log for log in logs
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
]
# Date filter
if startDate or endDate:
try:
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
# Filter by username
if user_name:
logs = [log for log in logs if user_name.lower() in log.get("user", "").lower()]
return logs
def update_log(self, index, action=None, details=None):
"""Update a specific log entry by index (0-based)."""
logs = self.get_all_logs()
if 0 <= index < len(logs):
if action:
logs[index]["action"] = action
if details:
logs[index]["details"] = details
self._rewrite_logs_file(logs)
return True
return False
def delete_log(self, index):
"""Delete a specific log entry by index (0-based)."""
logs = self.get_all_logs()
if 0 <= index < len(logs):
logs.pop(index)
self._rewrite_logs_file(logs)
return True
return False
# ------------------- INTERNAL HELPER -------------------
def _rewrite_logs_file(self, logs):
"""Overwrite the log file with current logs."""
with open(self.filepath, "w", encoding="utf-8") as f:
for log in logs:
f.write(
f"Timestamp: {log['timestamp']} | "
f"User: {log['user']} | "
f"Action: {log['action']} | "
f"Details: {log['details']}\n"
)
filtered_logs = [
log for log in filtered_logs
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
]
except Exception as e:
print("Date filter error:", e)
# Username filter
if userName:
filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()]
return filtered_logs

View File

@@ -1,456 +1,137 @@
import openpyxl
from openpyxl.styles import Font, PatternFill
import config
from flask_login import current_user
from model.Log import LogHelper
from model.Report import ReportHelper
from services.Generalservice import GeneralUse
from model.FolderAndFile import FolderAndFile
from model.Report import ReportHelper
class PmcReport:
data=[]
@staticmethod
def get_pmc_report(pmc_no):
# @staticmethod
# def get_pmc_report(pmc_no):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True, buffered=True)
try:
# try:
# pmc_info = GeneralUse.execute_sp(cursor, 'GetContractorInfoByPmcNo', [pmc_no], True)
# cursor.callproc("Get_pmc_hold_types", (pmc_no, pmc_info["Contractor_Id"]))
# hold_types = next(cursor.stored_results()).fetchall()
# cursor.callproc("GetContractorInfoByPmcNo", (pmc_no,))
# pmc_info = next(cursor.stored_results()).fetchone()
pmc_info = ReportHelper.execute_sp(cursor, 'GetContractorInfoByPmcNo', [pmc_no], True)
# # Extract hold_type_ids
# hold_type_ids = [ht['hold_type_id'] for ht in hold_types]
if not pmc_info:
return None
# invoices = []
# hold_type_ids_str = ",".join(map(str, hold_type_ids))
# cursor.callproc('GetInvoices_WithHold',[pmc_no, pmc_info["Contractor_Id"], hold_type_ids_str])
# for result in cursor.stored_results():
# invoices = result.fetchall()
cursor.callproc("Get_pmc_hold_types", (pmc_no, pmc_info["Contractor_Id"]))
hold_types = next(cursor.stored_results()).fetchall()
# Extract hold_type_ids
hold_type_ids = [ht['hold_type_id'] for ht in hold_types]
invoices = []
hold_amount_total = 0
if hold_type_ids:
hold_type_ids_str = ",".join(map(str, hold_type_ids))
cursor.callproc(
'GetInvoices_WithHold',
[pmc_no, pmc_info["Contractor_Id"], hold_type_ids_str]
)
else:
cursor.callproc(
'GetInvoices_NoHold',
[pmc_no, pmc_info["Contractor_Id"]]
)
for result in cursor.stored_results():
invoices = result.fetchall()
if hold_type_ids:
hold_amount_total = sum(row.get('hold_amount', 0) or 0 for row in invoices)
total_invo_final = sum(row.get('Final_Amount', 0) or 0 for row in invoices)
# GST RELEASE
# cursor.callproc('GetGSTReleaseByPMC', [pmc_no])
# gst_rel = []
# for result in cursor.stored_results():
# gst_rel = result.fetchall()
# gst_rel = GeneralUse.execute_sp(cursor, 'GetGSTReleaseByPMC', [pmc_no])
# hold_release = GeneralUse.execute_sp(cursor, 'GetHoldReleaseByPMC', [pmc_no])
gst_rel = ReportHelper.execute_sp(cursor, 'GetGSTReleaseByPMC', [pmc_no])
# credit_note = GeneralUse.execute_sp(cursor, 'GetCreditNoteByPMC', [pmc_no])
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)
# payments = GeneralUse.execute_sp(cursor, 'GetPaymentsByPMC', [pmc_no])
# totals = {
# "sum_invo_basic_amt": sum(row.get('Basic_Amount', 0) or 0 for row in invoices),
# "sum_invo_debit_amt": sum(row.get('Debit_Amount', 0) or 0 for row in invoices),
# "sum_invo_after_debit_amt": sum(row.get('After_Debit_Amount', 0) or 0 for row in invoices),
# "sum_invo_amt": sum(row.get('Amount', 0) or 0 for row in invoices),
# "sum_invo_gst_amt": sum(row.get('GST_Amount', 0) or 0 for row in invoices),
# "sum_invo_tds_amt": sum(row.get('TDS_Amount', 0) or 0 for row in invoices),
# "sum_invo_ds_amt": sum(row.get('SD_Amount', 0) or 0 for row in invoices),
# "sum_invo_on_commission": sum(row.get('On_Commission', 0) or 0 for row in invoices),
# "sum_invo_hydro_test": sum(row.get('Hydro_Testing', 0) or 0 for row in invoices),
# "sum_invo_gst_sd_amt": sum(row.get('GST_SD_Amount', 0) or 0 for row in invoices),
# "sum_invo_final_amt": sum(row.get('Final_Amount', 0) or 0 for row in invoices),
# "sum_invo_hold_amt": sum(row.get('hold_amount', 0) or 0 for row in invoices),
# "sum_gst_basic_amt": sum(row.get('basic_amount', 0) or 0 for row in gst_rel),
# "sum_gst_final_amt": sum(row.get('final_amount', 0) or 0 for row in gst_rel),
# "sum_pay_payment_amt": sum(row.get('Payment_Amount', 0) or 0 for row in payments),
# "sum_pay_tds_payment_amt": sum(row.get('TDS_Payment_Amount', 0) or 0 for row in payments),
# "sum_pay_total_amt": sum(row.get('Total_amount', 0) or 0 for row in payments)
# }
# ---------------- HOLD RELEASE ----------------
# cursor.callproc('GetHoldReleaseByPMC', [pmc_no])
# hold_release = []
# for result in cursor.stored_results():
# hold_release = result.fetchall()
# return {
# "info": pmc_info,
# "invoices": invoices,
# "hold_types": hold_types,
# "gst_rel": gst_rel,
# "payments": payments,
# "credit_note": credit_note,
# "hold_release": hold_release,
# "total": totals
# }
hold_release = ReportHelper.execute_sp(cursor, 'GetHoldReleaseByPMC', [pmc_no])
# finally:
# cursor.close()
# connection.close()
# ---------------- CREDIT NOTE ----------------
# cursor.callproc('GetCreditNoteByPMC', [pmc_no])
# credit_note = []
# for result in cursor.stored_results():
# credit_note = result.fetchall()
credit_note = ReportHelper.execute_sp(cursor, 'GetCreditNoteByPMC', [pmc_no])
payments = ReportHelper.execute_sp(cursor, 'GetPaymentsByPMC', [pmc_no])
# ---------------- PAYMENTS ----------------
# cursor.callproc('GetPaymentsByPMC', [pmc_no])
# payments = []
# for result in cursor.stored_results():
# payments = result.fetchall()
total_pay_amount = sum(row.get('Payment_Amount', 0) or 0 for row in payments)
total_pay_total = sum(row.get('Total_amount', 0) or 0 for row in payments)
totals = {
"sum_invo_basic_amt": sum(row.get('Basic_Amount', 0) or 0 for row in invoices),
"sum_invo_debit_amt": sum(row.get('Debit_Amount', 0) or 0 for row in invoices),
"sum_invo_after_debit_amt": sum(row.get('After_Debit_Amount', 0) or 0 for row in invoices),
"sum_invo_amt": sum(row.get('Amount', 0) or 0 for row in invoices),
"sum_invo_gst_amt": sum(row.get('GST_Amount', 0) or 0 for row in invoices),
"sum_invo_tds_amt": sum(row.get('TDS_Amount', 0) or 0 for row in invoices),
"sum_invo_ds_amt": sum(row.get('SD_Amount', 0) or 0 for row in invoices),
"sum_invo_on_commission": sum(row.get('On_Commission', 0) or 0 for row in invoices),
"sum_invo_hydro_test": sum(row.get('Hydro_Testing', 0) or 0 for row in invoices),
"sum_invo_gst_sd_amt": sum(row.get('GST_SD_Amount', 0) or 0 for row in invoices),
"sum_invo_final_amt": total_invo_final,
"sum_invo_hold_amt": hold_amount_total,
"sum_gst_basic_amt": total_gst_basic,
"sum_gst_final_amt": total_gst_final,
"sum_pay_payment_amt": total_pay_amount,
"sum_pay_tds_payment_amt": sum(row.get('TDS_Payment_Amount', 0) or 0 for row in payments),
"sum_pay_total_amt": total_pay_total
}
return {
"info": pmc_info,
"invoices": invoices,
"hold_types": hold_types,
"gst_rel": gst_rel,
"payments": payments,
"credit_note": credit_note,
"hold_release": hold_release,
"total": totals
}
finally:
cursor.close()
connection.close()
@staticmethod
def download_pmc_report(pmc_no):
connection = config.get_db_connection()
if not connection:
return None
cursor = connection.cursor(dictionary=True)
try:
# filename
filename = f"PMC_Report_{pmc_no}.xlsx"
output_folder = FolderAndFile.get_download_folder()
output_file = FolderAndFile.get_download_path(filename)
# ================= DATA FETCH =================
contractor_info = ReportHelper.execute_sp(cursor, 'GetContractorDetailsByPMC', [pmc_no], "one")
if not contractor_info:
return None
hold_types = ReportHelper.execute_sp(cursor, 'GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
invoices = ReportHelper.execute_sp(cursor, 'GetInvoicesAndGstReleaseByPmcNo', [pmc_no])
credit_notes = ReportHelper.execute_sp(cursor, 'GetCreditNoteByContractor', [contractor_info["Contractor_Id"]])
hold_amounts = ReportHelper.execute_sp(cursor, 'GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
all_payments = ReportHelper.execute_sp(cursor, 'GetAllPaymentsByPMC', [pmc_no])
gst_releases = ReportHelper.execute_sp(cursor, 'GetGSTReleaseDetailsByPMC', [pmc_no])
# ================= DATA MAPPING =================
hold_data = {}
for h in hold_amounts:
hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
payments_map = {}
for pay in all_payments:
if pay['invoice_no']:
payments_map.setdefault(pay['invoice_no'], []).append(pay)
# ================= LOG =================
LogHelper.log_action(
"Download PMC Report",
f"User {current_user.id} Download PMC Report '{pmc_no}'"
)
# ================= EXCEL =================
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "PMC Report"
# HEADER INFO
sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD."])
sheet.append(["Contractor Name", contractor_info["Contractor_Name"]])
sheet.append(["State", contractor_info["State_Name"]])
sheet.append(["District", contractor_info["District_Name"]])
sheet.append(["Block", contractor_info["Block_Name"]])
sheet.append([])
base_headers = [
"PMC No","Village","Work Type","Invoice Details","Invoice Date","Invoice No",
"Basic Amount","Debit","After Debit Amount","GST","Amount","TDS",
"SD","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"
]
headers = base_headers + hold_headers + payment_headers
sheet.append(headers)
# STYLE
for cell in sheet[sheet.max_row]:
cell.font = Font(bold=True)
# DATA
seen_invoices = set()
for inv in invoices:
invoice_no = inv["Invoice_No"]
payments = payments_map.get(invoice_no, [])
if invoice_no in seen_invoices:
continue
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"]
]
# HOLD DATA
invoice_holds = hold_data.get(inv["Invoice_Id"], {})
for ht_id in hold_type_map.keys():
row.append(invoice_holds.get(ht_id, ""))
# PAYMENT DATA
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)
# AUTO WIDTH
for col in sheet.columns:
max_len = max((len(str(cell.value)) for cell in col if cell.value), default=0)
sheet.column_dimensions[col[0].column_letter].width = max_len + 2
# SAVE
workbook.save(output_file)
workbook.close()
return output_folder, filename
except Exception as e:
print(f"Error generating PMC report: {e}")
return None
finally:
cursor.close()
connection.close()
# @staticmethod
# def download_pmc_report(pmc_no):
# connection = config.get_db_connection()
# if not connection:
# return None
# cursor = connection.cursor(dictionary=True)
# # output_folder = "static/download"
# # output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx")
# output_folder = FolderAndFile.get_download_folder
# filename = f"PMC_Report_{pmc_no}.xlsx"
# output_file = FolderAndFile.get_download_path(filename)
# try:
# filename = f"PMC_Report_{pmc_no}.xlsx"
# cursor.callproc('GetContractorDetailsByPMC', [pmc_no])
# contractor_info = next(cursor.stored_results()).fetchone()
# output_folder = FolderAndFile.get_download_folder()
# output_file = FolderAndFile.get_download_path(filename)
# contractor_info = GeneralUse.execute_sp(cursor, 'GetContractorInfoByPmcNo', [pmc_no])
# contractor_info = contractor_info[0] if contractor_info else None
# print("contractor_info:::",contractor_info)
# if not contractor_info:
# return None
# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
# hold_types = next(cursor.stored_results()).fetchall()
# hold_types = GeneralUse.execute_sp(cursor, 'GetHoldTypesByContractor',[contractor_info["Contractor_Id"]])
# 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('GetCreditNoteByContractor',[contractor_info["Contractor_Id"]])
# credit_notes = []
# for result in cursor.stored_results():
# credit_notes = result.fetchall()
# invoices = GeneralUse.execute_sp(cursor, 'GetInvoicesByContractorOrPMCNo', [None,pmc_no])
# credit_notes = GeneralUse.execute_sp(cursor, 'NewGetCreditNotesByPMCNo', [pmc_no])
# credit_note_map = {}
# for cn in credit_notes:
# key = (cn["PMC_No"], cn["Invoice_No"])
# key = (str(cn['PMC_No']).strip())
# credit_note_map.setdefault(key, []).append(cn)
# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
# hold_amounts = next(cursor.stored_results()).fetchall()
# hold_amounts = GeneralUse.execute_sp(cursor, 'GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
# gst_releases = GeneralUse.execute_sp(cursor, 'GetGSTReleaseDetailsByPMC', [pmc_no])
# gst_release_map = {}
# for gr in gst_releases:
# key = (str(gr['PMC_No']).strip())
# gst_release_map.setdefault(key, []).append(gr)
# # ================= DATA MAPPING =================
# 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)
# # ---------------- GST RELEASE DETAILS ----------------
# cursor.callproc('GetGSTReleaseDetailsByPMC', [pmc_no])
# gst_releases = []
# for result in cursor.stored_results():
# gst_releases = result.fetchall()
# gst_release_map = {}
# for gr in gst_releases:
# invoice_nos = []
# if gr['Invoice_No']:
# cleaned = gr['Invoice_No'].replace(' ', '')
# if '&' in cleaned:
# invoice_nos = cleaned.split('&')
# elif ',' in cleaned:
# invoice_nos = cleaned.split(',')
# else:
# invoice_nos = [cleaned]
# for inv_no in invoice_nos:
# gst_release_map.setdefault(inv_no, []).append(gr)
# LogHelper.log_action(
# "Download PMC Report",
# f"User {current_user.id} Download PMC Report '{pmc_no}'"
# )
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "PMC Report"
# 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)
# 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()
# 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)
# workbook.save(output_file)
# workbook.close()
# # ================= LOG =================
# LogHelper.log_action("Download PMC Report",f"User {current_user.id} Download PMC Report '{pmc_no}'")
# ReportHelper.generate_excel(
# 0, contractor_info, invoices, hold_types, hold_data,
# credit_note_map,gst_release_map, output_file)
# return output_folder, filename
# finally:
# cursor.close()
# connection.close()
# connection.close()

View File

@@ -2,9 +2,10 @@ import config
from datetime import datetime
from flask import send_file
import openpyxl
from openpyxl.styles import Font
from openpyxl.styles import Font, PatternFill
from model.FolderAndFile import FolderAndFile
from services.Generalservice import GeneralUse
class ReportHelper:
isSuccess = False
@@ -16,30 +17,6 @@ class ReportHelper:
self.resultMessage = ""
self.data = []
@staticmethod
def execute_sp(cursor, proc_name, params=[], fetch_one=False):
cursor.callproc(proc_name, params)
return (
ReportHelper.fetch_one_result(cursor)
if fetch_one else
ReportHelper.fetch_all_results(cursor)
)
@staticmethod
def fetch_all_results(cursor):
data = []
for result in cursor.stored_results():
data = result.fetchall()
return data
@staticmethod
def fetch_one_result(cursor):
data = None
for result in cursor.stored_results():
data = result.fetchone()
return data
@staticmethod
def search_contractor(request):
@@ -59,10 +36,7 @@ class ReportHelper:
cursor = connection.cursor(dictionary=True)
try:
data = ReportHelper.execute_sp(
cursor,
"search_contractor_info",
[
data = GeneralUse.execute_sp(cursor,"search_contractor_info",[
subcontractor_name or None,
pmc_no or None,
state or None,
@@ -71,8 +45,7 @@ class ReportHelper:
village or None,
year_from or None,
year_to or None
]
)
])
except Exception as e:
print(f"Error in search_contractor: {e}")
@@ -84,192 +57,156 @@ class ReportHelper:
return data
@staticmethod
def get_contractor_report(contractor_id):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
try:
# Contractor Info (only one fetch)
contInfo = ReportHelper.execute_sp(cursor, 'GetContractorInfo', [contractor_id], True)
# Hold Types
hold_types = ReportHelper.execute_sp(cursor, 'GetContractorHoldTypes', [contractor_id])
# Invoices
invoices = ReportHelper.execute_sp(cursor, 'GetContractorInvoices', [contractor_id])
# GST Release
gst_rel = ReportHelper.execute_sp(cursor, 'GetGSTRelease', [contractor_id])
# Hold Release
hold_release = ReportHelper.execute_sp(cursor, 'GetHoldRelease', [contractor_id])
# Credit Note
credit_note = ReportHelper.execute_sp(cursor, 'GetCreditNote', [contractor_id])
# Payments
payments = ReportHelper.execute_sp(cursor, 'GetPayments', [contractor_id])
# Totals
total = {
"sum_invo_basic_amt": float(sum(row['Basic_Amount'] or 0 for row in invoices)),
"sum_invo_debit_amt": float(sum(row['Debit_Amount'] or 0 for row in invoices)),
"sum_invo_after_debit_amt": float(sum(row['After_Debit_Amount'] or 0 for row in invoices)),
"sum_invo_amt": float(sum(row['Amount'] or 0 for row in invoices)),
"sum_invo_gst_amt": float(sum(row['GST_Amount'] or 0 for row in invoices)),
"sum_invo_tds_amt": float(sum(row['TDS_Amount'] or 0 for row in invoices)),
"sum_invo_ds_amt": float(sum(row['SD_Amount'] or 0 for row in invoices)),
"sum_invo_on_commission": float(sum(row['On_Commission'] or 0 for row in invoices)),
"sum_invo_hydro_test": float(sum(row['Hydro_Testing'] or 0 for row in invoices)),
"sum_invo_gst_sd_amt": float(sum(row['GST_SD_Amount'] or 0 for row in invoices)),
"sum_invo_final_amt": float(sum(row['Final_Amount'] or 0 for row in invoices)),
"sum_invo_hold_amt": float(sum(row['hold_amount'] or 0 for row in invoices)),
"sum_gst_basic_amt": float(sum(row['basic_amount'] or 0 for row in gst_rel)),
"sum_gst_final_amt": float(sum(row['final_amount'] or 0 for row in gst_rel)),
"sum_pay_payment_amt": float(sum(row['Payment_Amount'] or 0 for row in payments)),
"sum_pay_tds_payment_amt": float(sum(row['TDS_Payment_Amount'] or 0 for row in payments)),
"sum_pay_total_amt": float(sum(row['Total_amount'] or 0 for row in payments))
}
current_date = datetime.now().strftime('%Y-%m-%d')
finally:
cursor.close()
connection.close()
return {
"contInfo": contInfo,
"invoices": invoices,
"hold_types": hold_types,
"gst_rel": gst_rel,
"payments": payments,
"credit_note": credit_note,
"hold_release": hold_release,
"total": total,
"current_date": current_date
}
@staticmethod
def download_report(contractor_id):
try:
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True)
def get_contractor_info(contractor_id):
from model.ContractorInfo import ContractorInfo
contractor = ContractorInfo(contractor_id)
return contractor.contInfo if contractor.contInfo else None
# -------- Contractor Info --------
contInfo = ReportHelper.execute_sp(cursor, 'GetContractorInfo', [contractor_id], True)
if not contInfo:
return "No contractor found", 404
# call this method for excel formate written
@staticmethod
def generate_excel(contractor_id, contInfo, invoices, hold_types, hold_data,
credit_note_map, gst_release_map, output_file):
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "Contractor Report"
# -------- Invoice Data --------
cursor.callproc('FetchInvoicesByContractor', [contractor_id])
# Contractor Info
for field, value in contInfo.items():
sheet.append([field.replace("_", " "), value])
sheet.append([])
invoices = []
for result in cursor.stored_results():
invoices.extend(result.fetchall())
# Headers
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"]
if not invoices:
return "No invoice data found"
hold_headers = [ht['hold_type'] for ht in hold_types]
# -------- Create Workbook --------
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "Contractor Report"
payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# ================= CONTRACTOR DETAILS =================
sheet.append(["SUB CONTRACTOR DETAILS"])
sheet.cell(row=sheet.max_row, column=1).font = Font(bold=True)
sheet.append([])
all_headers = base_headers + hold_headers + payment_headers
sheet.append(all_headers)
sheet.append(["Name", contInfo.get("Contractor_Name") or ""])
sheet.append(["Mobile No", contInfo.get("Mobile_No") or ""])
sheet.append(["Email", contInfo.get("Email") or ""])
sheet.append(["Village", contInfo.get("Village_Name") or ""])
sheet.append(["Block", contInfo.get("Block_Name") or ""])
sheet.append(["District", contInfo.get("District_Name") or ""])
sheet.append(["State", contInfo.get("State_Name") or ""])
sheet.append(["Address", contInfo.get("Address") or ""])
sheet.append(["GST No", contInfo.get("GST_No") or ""])
sheet.append(["PAN No", contInfo.get("PAN_No") or ""])
sheet.append([])
sheet.append([])
for cell in sheet[sheet.max_row]:
cell.font = Font(bold=True)
cell.fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
# ================= TABLE HEADERS =================
headers = [
"PMC No", "Village", "Invoice No", "Invoice Date", "Work Type","Invoice_Details",
"Basic Amount", "Debit Amount", "After Debit Amount",
"Amount", "GST Amount", "TDS Amount", "SD Amount",
"On Commission", "Hydro Testing", "Hold Amount",
"GST SD Amount", "Final Amount",
"Payment Amount", "TDS Payment",
"Total Amount", "UTR"
processed_gst_releases = set()
appended_credit_keys = set()
previous_pmc_no = None
for inv in invoices:
pmc_no = str(inv["PMC_No"]).strip()
invoice_no = (
inv["invoice_no"].replace(" ", "") if inv["invoice_no"] else ""
if inv["invoice_no"] not in (None, "", 0)
else ""
)
key = (pmc_no)
# Yellow separator
if previous_pmc_no and pmc_no != previous_pmc_no:
sheet.append([""] * len(all_headers))
yellow_fill = PatternFill(start_color="FFFF99", end_color="FFFF99", fill_type="solid")
for cell in sheet[sheet.max_row]:
cell.fill = yellow_fill
previous_pmc_no = pmc_no
# Invoice Row
row = [
pmc_no,
inv.get("Village_Name", ""),
inv.get("Work_Type", ""),
inv.get("Invoice_Details", ""),
inv.get("Invoice_Date", ""),
# inv.get("invoice_no",""),
invoice_no,
inv.get("Basic_Amount", ""),
inv.get("Debit_Amount", ""),
inv.get("After_Debit_Amount", ""),
inv.get("GST_Amount", ""),
inv.get("Amount", ""),
inv.get("TDS_Amount", ""),
inv.get("SD_Amount", ""),
inv.get("On_Commission", ""),
inv.get("Hydro_Testing", ""),
inv.get("GST_SD_Amount", "")
]
sheet.append(headers)
for col in range(1, len(headers) + 1):
sheet.cell(row=sheet.max_row, column=col).font = Font(bold=True)
# ================= DATA =================
total_final = 0
total_payment = 0
total_amount = 0
# Hold values
invoice_holds = hold_data.get(inv["Invoice_Id"], {})
for ht_id in [ht['hold_type_id'] for ht in hold_types]:
row.append(invoice_holds.get(ht_id, ""))
for inv in invoices:
row = [
inv.get("PMC_No"),
inv.get("Village_Name"),
inv.get("invoice_no"),
inv.get("Invoice_Date"),
inv.get("Work_Type"),
inv.get("Invoice_Details"),
inv.get("Basic_Amount"),
inv.get("Debit_Amount"),
inv.get("After_Debit_Amount"),
inv.get("Amount"),
inv.get("GST_Amount"),
inv.get("TDS_Amount"),
inv.get("SD_Amount"),
inv.get("On_Commission"),
inv.get("Hydro_Testing"),
inv.get("Hold_Amount"),
inv.get("GST_SD_Amount"),
inv.get("Final_Amount"),
inv.get("Payment_Amount"),
inv.get("TDS_Payment_Amount"),
inv.get("Total_Amount"),
inv.get("UTR")
]
# Payment values
row += [
inv.get("Final_Amount", ""),
inv.get("Payment_Amount", ""),
inv.get("TDS_Payment_Amount", ""),
inv.get("Total_Amount", ""),
inv.get("UTR", "")
]
total_final += float(inv.get("Final_Amount") or 0)
total_payment += float(inv.get("Payment_Amount") or 0)
total_amount += float(inv.get("Total_Amount") or 0)
sheet.append(row)
sheet.append(row)
# GST Releases
if key in gst_release_map and key not in processed_gst_releases:
for gr in gst_release_map[key]:
gst_row = [
pmc_no, "", "", "GST Release Note", "", gr.get("Invoice_No", ""),
gr.get("Basic_Amount", ""), "", "", "", "", "", "", "", "", ""
]
# ================= TOTAL ROW =================
sheet.append([])
sheet.append([
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"TOTAL",
total_final,
total_payment,
"",
total_amount,
""
])
gst_row += [""] * len(hold_headers)
# ================= AUTO WIDTH =================
for column in sheet.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
if cell.value:
max_length = max(max_length, len(str(cell.value)))
sheet.column_dimensions[column_letter].width = max_length + 2
gst_row += [
gr.get("Final_Amount", ""),
"",
"",
gr.get("Total_Amount", ""),
gr.get("UTR", "")
]
sheet.append(gst_row)
processed_gst_releases.add(key)
# Credit Notes
if key in credit_note_map and key not in appended_credit_keys:
for cn in credit_note_map[key]:
cn_row = [
pmc_no, "", "", cn.get("Invoice_Details", "Credit Note"), "",
cn.get("Invoice_No", ""),
cn.get("Basic_Amount", ""),
cn.get("Debit_Amount", ""),
cn.get("After_Debit_Amount", ""),
cn.get("GST_Amount", ""),
cn.get("Amount", ""),
"", "", "", "", ""
]
cn_row += [""] * len(hold_headers)
cn_row += [
cn.get("Final_Amount", ""),
"",
"",
cn.get("Total_Amount", ""),
cn.get("UTR", "")
]
sheet.append(cn_row)
appended_credit_keys.add(key)
# SAVE ONCE AT END
workbook.save(output_file)
# ================= SAVE FILE =================
filename = f"Contractor_Report_{contInfo.get('Contractor_Name')}.xlsx"
output_file = FolderAndFile.get_download_path(filename)
workbook.save(output_file)
return send_file(output_file, as_attachment=True)
except Exception as e:
return str(e)

View File

@@ -1,7 +1,6 @@
from model.Utilities import ItemCRUDType
from model.ItemCRUD import ItemCRUD
class Subcontractor:
def __init__(self):
self.isSuccess = False

View File

@@ -9,10 +9,11 @@ class ItemCRUDType(Enum):
HoldType = 5
Subcontractor = 6
GSTRelease = 7
Invoice = 8
class RegEx:
patternAlphabetOnly = "^[A-Za-z ]+$"
allPattern = "^(?!\s*$).+"
patternAlphabetOnly = r"^[A-Za-z ]+$"
allPattern = r"^(?!\s*$).+"
class ResponseHandler:

View File

@@ -13,14 +13,14 @@ class Village:
def __init__(self):
self.isSuccess = False
self.resultMessage = ""
self.response = {} # ✅ ADDED
self.response = {}
self.village = ItemCRUD(itemType=ItemCRUDType.Village)
# 🔹 Helper: sync status
def _set_status(self, village):
self.isSuccess = village.isSuccess
# UPDATED (safe handling)
# UPDATED (safe handling)
if hasattr(village, "response"):
self.response = village.response
self.resultMessage = village.response.get("message", "")
@@ -37,7 +37,7 @@ class Village:
block_id, village_name = self._get_form_data(request)
if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.response = ResponseHandler.invalid_name("village")
self.resultMessage = self.response["message"]
self.isSuccess = False
return
@@ -75,7 +75,7 @@ class Village:
block_id, village_name = self._get_form_data(request)
if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.response = ResponseHandler.invalid_name("village")
self.resultMessage = self.response["message"]
self.isSuccess = False
return None
@@ -113,7 +113,7 @@ class Village:
block_id, village_name = self._get_form_data(request)
if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.response = ResponseHandler.invalid_name("village")
self.resultMessage = self.response["message"]
self.isSuccess = False
return
@@ -176,7 +176,7 @@ class Village:
print(f"Error fetching blocks: {e}")
self.isSuccess = False
# FIXED (removed jsonify response)
# FIXED (removed jsonify response)
self.response = ResponseHandler.fetch_failure("block")
self.resultMessage = self.response["message"]

View File

@@ -8,12 +8,18 @@ class GSTRelease:
def __init__(self):
self.isSuccess = False
self.resultMessage = ""
# ------------------- Add GST Release -------------------
def AddGSTRelease(self, request):
try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Print the full form data
print("===== DEBUG: FORM DATA =====")
for key, value in request.form.items():
print(f"{key} : {value}")
print("=============================")
data = {
"PMC_No": request.form.get("PMC_No", "").strip(),
"Invoice_No": request.form.get("Invoice_No", "").strip(),
@@ -24,6 +30,10 @@ class GSTRelease:
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
}
print("===== DEBUG: PARSED DATA =====")
print(data)
print("==============================")
# Add GST Release
gst.AddItem(
request=request,
@@ -31,12 +41,8 @@ class GSTRelease:
storedprocfetch="CheckGSTReleaseExists",
storedprocadd="AddGSTReleaseFromExcel"
)
# Check if addition was successful
if gst.isSuccess:
print(f"GST Release Added: {data}")
else:
print(f"Failed to add GST Release: {gst.resultMessage}")
print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
self.isSuccess = gst.isSuccess
self.resultMessage = str(gst.resultMessage)
@@ -48,20 +54,26 @@ class GSTRelease:
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
# ------------------- Edit GST Release -------------------
def EditGSTRelease(self, request, gst_release_id):
try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Map form inputs to stored procedure parameters
data = {
"PMC_No": request.form.get("PMC_No", "").strip(),
"Invoice_No": request.form.get("Invoice_No", "").strip(),
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
"Total_Amount": float(request.form.get("Total_Amount", 0) or 0),
"UTR": request.form.get("UTR", "").strip()
"p_pmc_no": request.form.get("PMC_No", "").strip(),
"p_invoice_no": request.form.get("invoice_no", "").strip(),
"p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
"p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
"p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
"p_utr": request.form.get("UTR", "").strip(),
"p_gst_release_id": gst_release_id
}
print("===== DEBUG: UPDATE DATA =====")
print(data)
print("==============================")
# Call your stored procedure
gst.EditItem(
request=request,
childid=gst_release_id,
@@ -77,8 +89,6 @@ class GSTRelease:
self.isSuccess = False
self.resultMessage = str(e)
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
# ------------------- Delete GST Release -------------------
def DeleteGSTRelease(self, gst_release_id):
try:

View File

@@ -2,7 +2,6 @@ import config
import mysql.connector
import config
import mysql.connector
from enum import Enum
from model.Utilities import ItemCRUDType
class Paymentmodel:
@@ -34,40 +33,44 @@ class Paymentmodel:
return payments
@staticmethod
def insert_payment(pmc_no, invoice_no, amount, tds_amount, total_amount, utr):
def insert_payment(subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr):
connection = Paymentmodel.get_connection()
if not connection:
return False
try:
cursor = connection.cursor()
cursor.callproc('InsertPayments', [pmc_no, invoice_no, amount, tds_amount, total_amount, utr])
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error inserting payment: {e}")
return False
finally:
cursor.close()
connection.close()
@staticmethod
def update_inpayment(subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr):
connection = Paymentmodel.get_connection()
if not connection:
return False
cursor = None
try:
cursor = connection.cursor()
cursor.callproc('UpdateInpaymentRecord', [
subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr
])
cursor.callproc('GetInvoiceId', [subcontractor_id, pmc_no, invoice_no])
invoice_id = None
for result in cursor.stored_results():
row = result.fetchone()
if row:
invoice_id = row[0]
if not invoice_id:
return False
cursor.callproc(
'InsertPayments',
[pmc_no, invoice_no, amount, tds_amount, total_amount, utr, invoice_id]
)
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error updating inpayment: {e}")
except Exception as e:
print(e)
return False
finally:
cursor.close()
connection.close()
if cursor:
cursor.close()
if connection:
connection.close()
@staticmethod
def fetch_payment_by_id(payment_id):
@@ -133,9 +136,7 @@ class Paymentmodel:
# Delete payment
cursor.callproc("DeletePayment", (payment_id,))
connection.commit()
# Reset inpayment fields
cursor.callproc("ResetInpayment", [pmc_no, invoice_no])
connection.commit()
return True, pmc_no, invoice_no
except mysql.connector.Error as e:
print(f"Error deleting payment: {e}")

View File

@@ -0,0 +1,30 @@
class GeneralUse:
data=[]
@staticmethod
def execute_sp(cursor, proc_name, params=[], fetch_one=False):
cursor.callproc(proc_name, params)
return (
GeneralUse.fetch_one_result(cursor)
if fetch_one else
GeneralUse.fetch_all_results(cursor)
)
@staticmethod
def fetch_all_results(cursor):
data = []
for result in cursor.stored_results():
data = result.fetchall()
return data
@staticmethod
def fetch_one_result(cursor):
data = None
for result in cursor.stored_results():
data = result.fetchone()
return data

179
services/ReportService.py Normal file
View File

@@ -0,0 +1,179 @@
import config
from model.FolderAndFile import FolderAndFile
from services.Generalservice import GeneralUse
from model.Report import ReportHelper
from decimal import Decimal
def safe_decimal(value):
if value is None:
return Decimal(0)
return Decimal(value)
class ReportService:
def __init__(self, contractor_id=None, pmc_no=None):
self.contractor_id = contractor_id
self.pmc_no = pmc_no
self.contInfo = None
self.hold_types = []
self.invoices = []
self.hold_amounts = []
self.hold_data = {}
self.credit_note_raw = []
self.credit_note_map = {}
self.gst_release_raw = []
self.gst_release_map = {}
self.output_file = None
# ---------------- LOAD DATA ----------------
def load_data(self):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
try:
# ---------- CONTRACTOR ----------
if self.contractor_id:
self.contInfo = GeneralUse.execute_sp(cursor, 'GetContractorInfo', [self.contractor_id], True)
self.hold_types = GeneralUse.execute_sp(cursor, 'HoldTypesByContractorId', [self.contractor_id])
self.invoices = GeneralUse.execute_sp(cursor, 'GetInvoicesByContractorOrPMCNo', [self.contractor_id, None])
self.gst_release_raw = GeneralUse.execute_sp(cursor, 'GetGSTRelease', [self.contractor_id])
# ---------- PMC ----------
elif self.pmc_no:
self.contInfo = GeneralUse.execute_sp(cursor, 'GetContractorInfoByPmcNo', [self.pmc_no], True)
self.contractor_id = self.contInfo["Contractor_Id"]
self.hold_types = GeneralUse.execute_sp(cursor, 'GetHoldTypesByContractor', [self.contractor_id])
self.invoices = GeneralUse.execute_sp(cursor, 'GetInvoicesByContractorOrPMCNo', [None, self.pmc_no])
self.gst_release_raw = GeneralUse.execute_sp(cursor, 'GetGSTReleaseDetailsByPMC', [self.pmc_no])
# ---------- COMMON ----------
self.hold_amounts = GeneralUse.execute_sp(cursor, 'GetHoldAmountsByContractor', [self.contractor_id])
self.credit_note_raw = GeneralUse.execute_sp(cursor, 'GetCreditNotesByContractor', [self.contractor_id])
# self.gst_release_raw = GeneralUse.execute_sp(cursor, 'GetGSTRelease', [self.pmc_no, self.contractor_id])
self.prepare_maps()
self.total = self.calculate_totals()
finally:
cursor.close()
connection.close()
return self
# ---------------- MAPS ----------------
def prepare_maps(self):
# HOLD MAP
for h in self.hold_amounts:
self.hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
# CREDIT MAP
for cn in self.credit_note_raw:
key = str(cn.get('PMC_No') or cn.get('pmc_no')).strip()
self.credit_note_map.setdefault(key, []).append(cn)
# GST MAP (FIXED)
for gr in self.gst_release_raw:
pmc_value = gr.get('PMC_No') or gr.get('pmc_no') or gr.get('PMC_NO')
if not pmc_value:
continue # skip if missing
key = str(pmc_value).strip()
self.gst_release_map.setdefault(key, []).append(gr)
# ---------------- calculate total ----------------
def calculate_totals(self):
total = {
"sum_invo_basic_amt": Decimal(0),
"sum_invo_debit_amt": Decimal(0),
"sum_invo_after_debit_amt": Decimal(0),
"sum_invo_gst_amt": Decimal(0),
"sum_invo_amt": Decimal(0),
"sum_invo_tds_amt": Decimal(0),
"sum_invo_ds_amt": Decimal(0),
"sum_invo_on_commission": Decimal(0),
"sum_invo_hydro_test": Decimal(0),
"sum_invo_hold_amt": Decimal(0),
"sum_invo_gst_sd_amt": Decimal(0),
"sum_invo_final_amt": Decimal(0),
"sum_gst_basic_amt": Decimal(0),
"sum_gst_final_amt": Decimal(0),
"sum_pay_payment_amt": Decimal(0),
"sum_pay_tds_payment_amt": Decimal(0),
"sum_pay_total_amt": Decimal(0)
}
# ---------- INVOICE ----------
for inv in self.invoices:
total["sum_invo_basic_amt"] += safe_decimal(inv.get("Basic_Amount"))
total["sum_invo_debit_amt"] += safe_decimal(inv.get("Debit_Amount"))
total["sum_invo_after_debit_amt"] += safe_decimal(inv.get("After_Debit_Amount"))
total["sum_invo_gst_amt"] += safe_decimal(inv.get("GST_Amount"))
total["sum_invo_amt"] += safe_decimal(inv.get("Amount"))
total["sum_invo_tds_amt"] += safe_decimal(inv.get("TDS_Amount"))
total["sum_invo_ds_amt"] += safe_decimal(inv.get("SD_Amount"))
total["sum_invo_on_commission"] += safe_decimal(inv.get("On_Commission"))
total["sum_invo_hydro_test"] += safe_decimal(inv.get("Hydro_Testing"))
total["sum_invo_gst_sd_amt"] += safe_decimal(inv.get("GST_SD_Amount"))
total["sum_invo_final_amt"] += safe_decimal(inv.get("Final_Amount"))
total["sum_invo_hold_amt"] += safe_decimal(inv.get("hold_amount"))
# ---------- GST ----------
for gst in self.gst_release_raw:
total["sum_gst_basic_amt"] += safe_decimal(gst.get("basic_amount"))
total["sum_gst_final_amt"] += safe_decimal(gst.get("final_amount"))
# ---------- PAYMENTS ----------
if hasattr(self, "payments"):
for pay in self.payments:
total["sum_pay_payment_amt"] += safe_decimal(pay.get("Payment_Amount"))
total["sum_pay_tds_payment_amt"] += safe_decimal(pay.get("TDS_Payment_Amount"))
total["sum_pay_total_amt"] += safe_decimal(pay.get("Total_amount"))
return total
# ---------------- WEB DATA ----------------
def get_web_data(self):
return {
"contInfo": self.contInfo,
"invoices": self.invoices,
"hold_types": self.hold_types,
"credit_note": self.credit_note_raw,
"gst_rel": self.gst_release_raw,
"total": self.total
}
# ---------------- DOWNLOAD ----------------
def download_excel(self):
if not self.contInfo:
return None, "No data found"
filename = f"Report_{self.contractor_id or self.pmc_no}.xlsx"
self.output_file = FolderAndFile.get_download_path(filename=filename)
ReportHelper.generate_excel(
self.contractor_id or 0,
self.contInfo,
self.invoices,
self.hold_types,
self.hold_data,
self.credit_note_map,
self.gst_release_map,
self.output_file
)
return self.output_file, None

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -1,43 +1,57 @@
$(document).ready(function () {
function fetchResults() {
let formData = $('#search-form').serialize();
document.addEventListener("DOMContentLoaded", function () {
$.ajax({
type: 'POST',
url: '/search_contractor',
data: formData,
success: function (data) {
let tableBody = $('#result-table tbody');
tableBody.empty();
const form = document.getElementById("search-form");
const tableBody = document.querySelector("#result-table tbody");
if (data.length === 0) {
tableBody.append('<tr><td colspan="6">No data found</td></tr>');
} else {
data.forEach(function (row) {
tableBody.append(`
<tr>
<td><a href="/contractor_report/${row.Contractor_Id}" target="_blank">${row.Contractor_Name}</a></td>
<td><a href="/pmc_report/${row.PMC_No}" target="_blank">${row.PMC_No}</a></td>
<td>${row.State_Name}</td>
<td>${row.District_Name}</td>
<td>${row.Block_Name}</td>
<td>${row.Village_Name}</td>
</tr>
`);
});
}
},
error: function (xhr) {
alert(xhr.responseJSON.error);
}
function fetchData(page = 1) {
const formData = new FormData(form);
formData.append("page", page);
fetch("/search_contractor", {
method: "POST",
body: formData
})
.then(res => res.json())
.then(res => {
tableBody.innerHTML = "";
res.data.forEach(row => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="contractor-link" data-id="${row.Contractor_Id}">
${row.Contractor_Name}
</td>
<td class="pmc-link" data-pmc="${row.PMC_No}">
${row.PMC_No}
</td>
<td>${row.State_Name}</td>
<td>${row.District_Name}</td>
<td>${row.Block_Name}</td>
<td>${row.Village_Name}</td>
`;
tableBody.appendChild(tr);
});
});
}
// Auto search
form.addEventListener("input", () => fetchData());
// Click Contractor
document.addEventListener("click", function (e) {
if (e.target.classList.contains("contractor-link")) {
const id = e.target.dataset.id;
window.location.href = `/contractor_report/${id}`;
}
$('#search-form input').on('keyup change', function () {
fetchResults();
});
if (e.target.classList.contains("pmc-link")) {
const pmc = e.target.dataset.pmc;
window.location.href = `/pmc_report/${pmc}`;
}
});
window.onload = function () {
document.getElementById('subcontractor_name').focus();
};
fetchData();
});

View File

@@ -1,26 +1,23 @@
// Search on table using search inpute options
function searchTable() {
let input = document.getElementById("searchBar").value.toLowerCase();
let rows = document.querySelectorAll("table tbody tr");
let tables = document.querySelectorAll("table");
rows.forEach(row => {
let blockName = row.cells[1].textContent.toLowerCase();
let districtName = row.cells[2].textContent.toLowerCase();
let villageName = row.cells[3].textContent.toLowerCase();
tables.forEach(table => {
let rows = table.querySelectorAll("tr");
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) {
row.style.display = "";
} else {
row.style.display = "none";
}
rows.forEach((row, index) => {
if (index === 0) return; // header skip
let text = row.textContent.toLowerCase();
row.style.display = text.includes(input) ? "" : "none";
});
});
}
// Common Sorting Script for Tables
function sortTable(n, dir) {
var table, rows, switching, i, x, y, shouldSwitch;
@@ -57,14 +54,14 @@ function sortTable(n, dir) {
}
// Attach sorting functionality to all sortable tables
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener("DOMContentLoaded", function () {
// Find all elements with the class "sortable-header"
var sortableHeaders = document.querySelectorAll(".sortable-header");
sortableHeaders.forEach(function(header) {
sortableHeaders.forEach(function (header) {
// Attach click event for ascending sort
if (header.querySelector(".sort-asc")) {
header.querySelector(".sort-asc").addEventListener("click", function() {
header.querySelector(".sort-asc").addEventListener("click", function () {
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
sortTable(columnIndex, "asc");
});
@@ -72,7 +69,7 @@ document.addEventListener("DOMContentLoaded", function() {
// Attach click event for descending sort
if (header.querySelector(".sort-desc")) {
header.querySelector(".sort-desc").addEventListener("click", function() {
header.querySelector(".sort-desc").addEventListener("click", function () {
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
sortTable(columnIndex, "desc");
});
@@ -105,4 +102,31 @@ document.addEventListener("DOMContentLoaded", function () {
displayButton.classList.add("active-button");
addButton.classList.remove("active-button");
});
});
document.addEventListener("DOMContentLoaded", function () {
let tables = document.querySelectorAll("table");
tables.forEach(table => {
let header = table.querySelector("tr:first-child");
if (header) {
header.style.position = "sticky";
header.style.top = "0";
header.style.background = "#fff";
header.style.zIndex = "2";
}
if (!table.parentElement.classList.contains("table-wrapper")) {
let wrapper = document.createElement("div");
wrapper.classList.add("table-wrapper");
wrapper.style.maxHeight = "65vh"
wrapper.style.overflowY = "auto";
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
}
});
});

View File

@@ -15,17 +15,21 @@
<button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button>
</div>
<div id="addForm" style="display: none;">
<h2>Add GST Release</h2>
<form action="/add_gst_release" method="POST" onsubmit="showSuccessAlert(event)">
<div class="row1">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
<div id="subcontractor_list" class="autocomplete-items"></div>
</div>
<div>
<label for="subcontractor">Subcontractor Name:</label>
<!-- Text input for user-friendly autocomplete -->
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
<!-- Hidden input for backend; must match model's Contractor_ID -->
<input type="hidden" id="subcontractor_id" name="Contractor_ID"/>
<div id="subcontractor_list" class="autocomplete-items"></div>
</div>
</div>
<label for="PMC_No">PMC No:</label><br>
@@ -37,19 +41,19 @@
</select><br><br>
<label for="invoice_No">Invoice No:</label><br>
<input type="text" id="invoice_No" name="invoice_No" required><br><br>
<input type="text" id="invoice_No" name="Invoice_No" required><br><br>
<label for="basic_amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required><br><br>
<input type="number" step="0.01" id="basic_amount" name="Basic_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="final_amount">Final Amount:</label><br>
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required><br><br>
<input type="number" step="0.01" id="final_amount" name="Final_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" placeholder="₹ - 00.00" required><br><br>
<input type="number" step="0.01" id="total_amount" name="Total_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr" required><br><br>
<input type="text" id="utr" name="UTR" required><br><br>
<button type="submit">Submit GST Release</button>
</form>
@@ -117,46 +121,61 @@
<script>
document.addEventListener('DOMContentLoaded', function() {
// Handle subcontractor autocomplete
document.getElementById("subcontractor").addEventListener("input", function () {
const subcontractorInput = document.getElementById("subcontractor");
const subcontractorIdInput = document.getElementById("subcontractor_id");
const subcontractorList = document.getElementById("subcontractor_list");
const pmcDropdown = document.getElementById("PMC_No");
const form = document.querySelector('form');
// --------------------------
// Subcontractor autocomplete
// --------------------------
subcontractorInput.addEventListener("input", function () {
const query = this.value;
const list = document.getElementById("subcontractor_list");
if (query.length < 2) {
list.innerHTML = '';
subcontractorList.innerHTML = '';
subcontractorIdInput.value = ''; // reset hidden id
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; // reset PMC dropdown
return;
}
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
list.innerHTML = '';
subcontractorList.innerHTML = '';
data.results.forEach(item => {
const div = document.createElement("div");
div.setAttribute("data-id", item.id);
div.textContent = item.name;
list.appendChild(div);
subcontractorList.appendChild(div);
});
});
});
// Handle subcontractor selection
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
// --------------------------
// Subcontractor selection
// --------------------------
subcontractorList.addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent;
if (selectedId) {
document.getElementById("subcontractor_id").value = selectedId;
document.getElementById("subcontractor").value = selectedName;
document.getElementById("subcontractor_list").innerHTML = "";
// Set hidden field for backend
subcontractorIdInput.value = selectedId;
// Update PMC dropdown for selected subcontractor
// Set text input to selected name
subcontractorInput.value = selectedName;
// Clear the autocomplete list
subcontractorList.innerHTML = "";
// Fetch and populate PMC dropdown
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
.then(response => response.json())
.then(data => {
const pmcDropdown = document.getElementById("PMC_No");
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
data.pmc_nos.forEach(pmc => {
const option = document.createElement("option");
option.value = pmc;
@@ -166,6 +185,22 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
});
// --------------------------
// Form submit validation
// --------------------------
form.addEventListener('submit', function(e) {
if (!subcontractorIdInput.value) {
e.preventDefault();
alert("Please select a subcontractor from the list.");
subcontractorInput.focus();
} else if (!pmcDropdown.value) {
e.preventDefault();
alert("Please select a PMC No.");
pmcDropdown.focus();
}
});
});
</script>

View File

@@ -9,37 +9,37 @@
<body>
<h2>Edit GST Release</h2>
<form action="/edit_gst_release/{{ gst_release_data[0] }}" method="POST">
<!-- <label for="invoice_id">Invoice Id:</label><br>-->
<!-- <input type="number" id="invoice_id" name="invoice_id" value="{{ gst_release_data[0] }}" required><br><br>-->
<form action="/edit_gst_release/{{ gst_release_data.gst_release_id }}" method="POST">
<!-- PMC Number -->
<label for="PMC_No">PMC No :</label><br>
<input type="number" id="PMC_No" name="PMC_No" value="{{ gst_release_data[1] }}" required><br><br>
<input type="text" id="PMC_No" name="PMC_No" value="{{ gst_release_data.pmc_no }}" required><br><br>
<label for="invoice_No">Invoice No:</label><br>
<input type="number" step="0.01" id="invoice_No" name="invoice_No" value="{{ gst_release_data[2] }}"
required><br><br>
<!-- Invoice Number -->
<label for="invoice_no">Invoice No:</label><br>
<input type="text" id="invoice_no" name="invoice_no" value="{{ gst_release_data.invoice_no }}" required><br><br>
<!-- Basic Amount -->
<label for="Basic_Amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="Basic_Amount" name="Basic_Amount" value="{{ gst_release_data.basic_amount }}" required><br><br>
<label for="basic_amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ gst_release_data[3] }}"
required><br><br>
<!-- Final Amount -->
<label for="Final_Amount">Final Amount:</label><br>
<input type="number" step="0.01" id="Final_Amount" name="Final_Amount" value="{{ gst_release_data.final_amount }}" required><br><br>
<label for="final_amount">Final Amount:</label><br>
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ gst_release_data[4] }}"
required><br><br>
<!-- Total Amount -->
<label for="Total_Amount">Total Amount:</label><br>
<input type="number" step="0.01" id="Total_Amount" name="Total_Amount" value="{{ gst_release_data.total_amount }}" required><br><br>
<label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" value="{{ gst_release_data[5] }}"
required><br><br>
<!-- UTR -->
<label for="UTR">UTR:</label><br>
<input type="text" id="UTR" name="UTR" value="{{ gst_release_data.utr }}" readonly required><br><br>
<label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr" value="{{ gst_release_data[6] }}"
required readonly><br><br>
<!-- Hidden Contractor ID -->
<input type="hidden" id="Contractor_ID" name="Contractor_ID" value="{{ gst_release_data.contractor_id }}">
<button type="submit">Update GST Release</button>
</form>
</body>
{% endblock %}

View File

@@ -7,6 +7,7 @@
<title>Edit Invoice</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style1.css') }}">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
@@ -204,15 +205,17 @@
type: "POST",
url: $(this).attr("action"),
data: $(this).serialize(),
dataType: 'json', // ensure JSON is returned
success: function (response) {
if (response.status === "success") {
$("#invoiceSuccessAlert").fadeIn().delay(3000).fadeOut();
alert("Invoice updated successfully!"); // <-- Popup alert
}
// ✅ Redirect to Add Invoice page (table part visible)
window.location.href = "{{ url_for('invoice.add_invoice') }}#addTable";
}
},
error: function (xhr) {
alert("Error: " + xhr.responseJSON.message);
alert("Error: " + xhr.responseJSON?.message || "Something went wrong!");
}
});
});

View File

@@ -34,7 +34,7 @@
<a href="/logout">Logout</a>
</div>
<div class="sidebar">
<img src="https://lceplpmprod.btltech.xyz/assets/images/lcpl.png" alt="logo-image" class="logo">
<img src="http://lceplpmprod.btltech.xyz/assets/images/lcpl.png" alt="logo-image" class="logo">
<ul class="nav-menu">
<li class="nav-item">
@@ -136,7 +136,7 @@
<div class="card">
<h2>Work Order</h2>
<!-- <a class="btn" href="/add_work_order">Go ➜</a> -->
<a class="btn">Go ➜</a>
<a class="btn">Go ➜</a>
</div>
<!-- <div class="card">
<h2>Purchase Order</h2>

View File

@@ -1,7 +1,6 @@
{% extends 'base.html' %}
{% block content %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -9,332 +8,337 @@
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">-->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/subcontractor_report.css') }}">
</head>
<body>
<div class="container">
<h2>PMC Report</h2>
<div class="container">
<h2>PMC Report</h2>
<div class="info">
<h2>Contractor Details</h2>
<div class="row2">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" value="{{ info.Contractor_Name }}"
readonly/>
<div class="info">
<h2>Contractor Details</h2>
<div class="row2">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" value="{{ contInfo.Contractor_Name }}"
readonly />
</div>
<div>
<label for="Address">Address :</label>
<textarea id="Address" name="Address" readonly>{{ contInfo.Address }}</textarea>
</div>
</div>
<div>
<label for="Address">Address :</label>
<textarea id="Address" name="Address" readonly>{{ info.Address }}</textarea>
<div class="row3">
<div>
<label for="PAN_No">PAN No :</label>
<input type="text" id="PAN_No" name="PAN_No" value="{{ contInfo.PAN_No }}" readonly />
</div>
<div>
<label for="Mobile_No">Mobile Number :</label>
<input type="text" id="Mobile_No" name="Mobile_No" value="{{ contInfo.Mobile_No }}" readonly />
</div>
<div>
<label for="Email">Email :</label>
<input type="text" id="Email" name="Email" value="{{ contInfo.Email }}" readonly />
</div>
</div>
</div>
<div class="row3">
<div>
<label for="PAN_No">PAN No :</label>
<input type="text" id="PAN_No" name="PAN_No" value="{{ info.PAN_No }}" readonly/>
</div>
<div>
<label for="Mobile_No">Mobile Number :</label>
<input type="text" id="Mobile_No" name="Mobile_No" value="{{ info.Mobile_No }}" readonly/>
</div>
<div>
<label for="Email">Email :</label>
<input type="text" id="Email" name="Email" value="{{ info.Email }}" readonly/>
</div>
</div>
<div class="row2">
<div>
<label for="GST_Registration_Type">GST Registration Type :</label>
<input type="text" id="GST_Registration_Type" name="GST_Registration_Type"
value="{{ info.GST_Registration_Type }}" readonly/>
<div class="row2">
<div>
<label for="GST_Registration_Type">GST Registration Type :</label>
<input type="text" id="GST_Registration_Type" name="GST_Registration_Type"
value="{{ contInfo.GST_Registration_Type }}" readonly />
</div>
<div>
<label for="GST_No">GST No:</label>
<input type="text" id="GST_No" name="GST_No" value="{{ contInfo.GST_No }}" readonly />
</div>
</div>
<div>
<label for="GST_No">GST No:</label>
<input type="text" id="GST_No" name="GST_No" value="{{ info.GST_No }}" readonly/>
<h2>PMC Report for PMC No: {{ contInfo.PMC_No}}</h2>
<div class="row3">
<div>
<label for="State">State :</label>
<input type="text" id="State" name="State" value="{{ contInfo.State_Name }}" readonly />
</div>
<div>
<label for="District">District :</label>
<input type="text" id="District" name="District" value="{{ contInfo.District_Name }}" readonly />
</div>
<div>
<label for="Block">Block :</label>
<input type="text" id="Block" name="Block" value="{{ contInfo.Block_Name }}" readonly />
</div>
</div>
</div>
<h2>PMC Report for PMC No: {{ info.PMC_No}}</h2>
<div class="row3">
<div>
<label for="State">State :</label>
<input type="text" id="State" name="State" value="{{ info.State_Name }}" readonly/>
</div>
<div>
<label for="District">District :</label>
<input type="text" id="District" name="District" value="{{ info.District_Name }}" readonly/>
</div>
<div>
<label for="Block">Block :</label>
<input type="text" id="Block" name="Block" value="{{ info.Block_Name }}" readonly/>
</div>
</div>
<div class="row2">
<div>
<label for="PMC_No">PMC No:</label>
<input type="text" id="PMC_No" name="PMC_No" value="{{ info.PMC_No }}" readonly/>
</div>
<div>
<label for="Village_Name">Village Name :</label>
<input type="text" id="Village_Name" name="Village_Name"
value="{{ info.Village_Name.capitalize() }}" readonly/>
<div class="row2">
<div>
<label for="PMC_No">PMC No:</label>
<input type="text" id="PMC_No" name="PMC_No" value="{{ contInfo.PMC_No }}" readonly />
</div>
<div>
<label for="Village_Name">Village Name :</label>
<input type="text" id="Village_Name" name="Village_Name"
value="{{ contInfo.Village_Name.capitalize() }}" readonly />
</div>
</div>
</div>
</div>
</div>
<h3>Invoice Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Village Name</th>
<th>Work Type</th>
<th>Invoice Details</th>
<th>Invoice Date</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST (18%)</th>
<th>Amount</th>
<th>TDS (1%)</th>
<th>SD (5%)</th>
<th>On Commission</th>
<th>Hydro Testing</th>
<h3>Invoice Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Village Name</th>
<th>Work Type</th>
<th>Invoice Details</th>
<th>Invoice Date</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST (18%)</th>
<th>Amount</th>
<th>TDS (1%)</th>
<th>SD (5%)</th>
<th>On Commission</th>
<th>Hydro Testing</th>
<!-- Dynamic Hold Types -->
{% set hold_types = invoices | map(attribute='hold_type') | reject('none') | unique | list %}
{% for hold in hold_types %}
<th>{{ hold }}</th>
{% endfor %}
<!-- Dynamic Hold Types -->
{% set hold_types = invoices | map(attribute='hold_type') | reject('none') | unique | list %}
{% for hold in hold_types %}
<th>{{ hold }}</th>
{% endfor %}
<th>GST SD (18%)</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for invoice in invoices %}
<tr>
<td>{{ invoice.PMC_No }}</td>
<td>{{ invoice.Village_Name.capitalize() }}</td>
<td>{{ invoice.Work_Type }}</td>
<td>{{ invoice.Invoice_Details }}</td>
<td>{{ invoice.Invoice_Date }}</td>
<td>{{ invoice.Invoice_No }}</td>
<td>{{ invoice.Basic_Amount }}</td>
<td>{{ invoice.Debit_Amount }}</td>
<td>{{ invoice.After_Debit_Amount }}</td>
<td>{{ invoice.GST_Amount }}</td>
<td>{{ invoice.Amount }}</td>
<td>{{ invoice.TDS_Amount }}</td>
<td>{{ invoice.SD_Amount }}</td>
<td>{{ invoice.On_Commission }}</td>
<td>{{ invoice.Hydro_Testing }}</td>
<th>GST SD (18%)</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for invoice in invoices %}
<tr>
<td>{{ invoice.PMC_No }}</td>
<td>{{ invoice.Village_Name.capitalize() if invoice.Village_Name else '' }}</td>
<td>{{ invoice.Work_Type }}</td>
<td>{{ invoice.Invoice_Details }}</td>
<td>{{ invoice.Invoice_Date }}</td>
<td>{{ invoice.invoice_no }}</td>
<td>{{ invoice.Basic_Amount }}</td>
<td>{{ invoice.Debit_Amount }}</td>
<td>{{ invoice.After_Debit_Amount }}</td>
<td>{{ invoice.GST_Amount }}</td>
<td>{{ invoice.Amount }}</td>
<td>{{ invoice.TDS_Amount }}</td>
<td>{{ invoice.SD_Amount }}</td>
<td>{{ invoice.On_Commission }}</td>
<td>{{ invoice.Hydro_Testing }}</td>
<!-- Dynamic Hold Amounts -->
{% for hold in hold_types %}
<td>
{% if invoice.hold_type == hold %}
{{ invoice.hold_amount }}
{% else %}
0
{% endif %}
</td>
{% endfor %}
<td>{{ invoice.GST_SD_Amount }}</td>
<td>{{ invoice.Final_Amount }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="6">Total</th>
<th>{{total["sum_invo_basic_amt"]}}</th>
<th>{{total["sum_invo_debit_amt"]}}</th>
<th>{{total["sum_invo_after_debit_amt"]}}</th>
<th>{{total["sum_invo_gst_amt"]}}</th>
<th>{{total["sum_invo_amt"]}}</th>
<th>{{total["sum_invo_tds_amt"]}}</th>
<th>{{total["sum_invo_ds_amt"]}}</th>
<th>{{total["sum_invo_on_commission"]}}</th>
<th>{{total["sum_invo_hydro_test"]}}</th>
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{total["sum_invo_hold_amt"]}}</th>
{% endfor %}
<th>{{total["sum_invo_gst_sd_amt"]}}</th>
<th>{{total["sum_invo_final_amt"]}}</th>
</tr>
<!-- Dynamic Hold Amounts -->
{% for hold in hold_types %}
<td>
{% if invoice.hold_type == hold %}
{{ invoice.hold_amount }}
{% else %}
0
<tr>
<td colspan="{{ 17 + hold_types|length }}">No invoices found.</td>
</tr>
{% endif %}
</td>
{% endfor %}
</tbody>
</table>
<h3>Hold Release</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{%if hold_release%}
{%for hold in hold_release%}
<tr>
<td>{{ hold['PMC_No'] }}</td>
<td>{{ hold['Invoice_No'] }}</td>
<td>{{ hold['Invoice_Details'] }}</td>
<td>{{ hold['Basic_Amount'] }}</td>
<td>{{ hold['Total_Amount'] }}</td>
<td>{{ hold['UTR'] }}</td>
</tr>
{%endfor%}
{%else%}
<tr>
<td colspan="6" style="text-align:center;">No data present</td>
</tr>
{%endif%}
</tbody>
</table>
<td>{{ invoice.GST_SD_Amount }}</td>
<td>{{ invoice.Final_Amount }}</td>
</tr>
<br>
<h3>GST Release Note Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Final Amount</th>
<th>Total_Amount</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if gst_rel %}
{% for gst in gst_rel %}
<tr>
<td>{{ gst.PMC_No }}</td>
<td>{{ gst.invoice_no }}</td>
<td>{{ gst.Basic_Amount }}</td>
<td>{{ gst.Final_Amount }}</td>
<td>{{ gst.Total_Amount }}</td>
<td>{{ gst.UTR }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
{% endfor %}
<tr>
<th colspan="6">Total</th>
<th>{{total["sum_invo_basic_amt"]}}</th>
<th>{{total["sum_invo_debit_amt"]}}</th>
<th>{{total["sum_invo_after_debit_amt"]}}</th>
<th>{{total["sum_invo_gst_amt"]}}</th>
<th>{{total["sum_invo_amt"]}}</th>
<th>{{total["sum_invo_tds_amt"]}}</th>
<th>{{total["sum_invo_ds_amt"]}}</th>
<th>{{total["sum_invo_on_commission"]}}</th>
<th>{{total["sum_invo_hydro_test"]}}</th>
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{total["sum_invo_hold_amt"]}}</th>
{% endfor %}
<th>{{total["sum_invo_gst_sd_amt"]}}</th>
<th>{{total["sum_invo_final_amt"]}}</th>
</tr>
<th>{{total["sum_gst_basic_amt"]}}</th>
<th>{{total["sum_gst_final_amt"]}}</th>
</tr>
{% else %}
<tr>
<td colspan="4">No GST release found.</td>
</tr>
{% endif %}
</tbody>
</table>
{% else %}
<tr>
<td colspan="{{ 17 + hold_types|length }}">No invoices found.</td>
</tr>
{% endif %}
</tbody>
</table>
<h3>Hold Release</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{%if hold_release%}
{%for hold in hold_release%}
<tr>
<td>{{ hold['PMC_No'] }}</td>
<td>{{ hold['Invoice_No'] }}</td>
<td>{{ hold['Invoice_Details'] }}</td>
<td>{{ hold['Basic_Amount'] }}</td>
<td>{{ hold['Total_Amount'] }}</td>
<td>{{ hold['UTR'] }}</td>
</tr>
{%endfor%}
{%else%}
<tr>
<td colspan="6" style="text-align:center;">No data present</td>
</tr>
{%endif%}
</tbody>
</table>
<br>
<h3>GST Release Note Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if gst_rel %}
{% for gst in gst_rel %}
<tr>
<td>{{ gst.pmc_no }}</td>
<td>{{ gst.invoice_no }}</td>
<td>{{ gst.basic_amount }}</td>
<td>{{ gst.final_amount }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_gst_basic_amt"]}}</th>
<th>{{total["sum_gst_final_amt"]}}</th>
</tr>
{% else %}
<tr>
<td colspan="4">No GST release found.</td>
</tr>
{% endif %}
</tbody>
</table>
<tr>
<h3>Credit Details</h3>
</tr>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST Amount</th>
<th>Amount</th>
<th>Final Amount</th>
<th>Payment Amount</th>
<th>Total Amount</th>
<th>UTR</th>
<tr>
<th>PMC No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST Amount</th>
<th>Amount</th>
<th>Final Amount</th>
<th>Payment Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
{% if credit_note %}
{% for credit in credit_note %}
<tr>
<td>{{ credit["PMC_No"] }}</td>
<td>{{ credit["Invoice_Details"] }}</td>
<td>{{ credit["Basic_Amount"] }}</td>
<td>{{ credit["Debit_Amount"] }}</td>
<td>{{ credit["After_Debit_Amount"] }}</td>
<td>{{ credit["GST_Amount"] }}</td>
<td>{{ credit["Amount"] }}</td>
<td>{{ credit["Final_Amount"] }}</td>
<td>{{ credit["Payment_Amount"] }}</td>
<td>{{ credit["Total_Amount"] }}</td>
<td>{{ credit["UTR"] }}</td>
{% if credit_note %}
{% for credit in credit_note %}
<tr>
<td>{{ credit["PMC_No"] }}</td>
<td>{{ credit["Invoice_Details"] }}</td>
<td>{{ credit["Basic_Amount"] }}</td>
<td>{{ credit["Debit_Amount"] }}</td>
<td>{{ credit["After_Debit_Amount"] }}</td>
<td>{{ credit["GST_Amount"] }}</td>
<td>{{ credit["Amount"] }}</td>
<td>{{ credit["Final_Amount"] }}</td>
<td>{{ credit["Payment_Amount"] }}</td>
<td>{{ credit["Total_Amount"] }}</td>
<td>{{ credit["UTR"] }}</td>
</tr>
{% endfor %}
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="11">No Credit note found.</td>
</tr>
{% endif %}
{% else %}
<tr>
<td colspan="11">No Credit note found.</td>
</tr>
{% endif %}
</thead>
</thead>
</table>
<br>
<h3>Payment Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Amount</th>
<th>TDS Amount @ 1% on BASIC AMOUNT</th>
<th>Total Amount Paid</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if payments %}
{% for pay in payments %}
<tr>
<td>{{ pay.pmc_no }}</td>
<td>{{ pay.invoice_no }}</td>
<td>{{ pay.Payment_Amount }}</td>
<td>{{ pay.TDS_Payment_Amount }}</td>
<td>{{ pay.Total_amount }}</td>
<td>{{ pay.utr}}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_pay_payment_amt"]}}</th>
<th>{{total["sum_pay_tds_payment_amt"]}}</th>
<th>{{total["sum_pay_total_amt"]}}</th>
<th></th>
</tr>
{% else %}
<br>
<h3>Payment Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Amount</th>
<th>TDS Amount @ 1% on BASIC AMOUNT</th>
<th>Total Amount Paid</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for pay in invoices %}
<tr>
<td>{{ pay.PMC_No }}</td>
<td>{{ pay.invoice_no }}</td>
<td>{{ pay.Payment_Amount }}</td>
<td>{{ pay.TDS_Payment_Amount }}</td>
<td>{{ pay.Total_Amount }}</td>
<td>{{ pay.UTR}}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_pay_payment_amt"]}}</th>
<th>{{total["sum_pay_tds_payment_amt"]}}</th>
<th>{{total["sum_pay_total_amt"]}}</th>
<th></th>
</tr>
{% else %}
<tr>
<td colspan="6">No payment found.</td>
</tr>
{% endif %}
<tr>
<td colspan="6">No payment found.</td>
</tr>
{% endif %}
</tbody>
</table>
</tbody>
</table>
<a href="/download_pmc_report/{{ info.PMC_No}}">
<button class="download-btn">Download PMC Report</button>
</a>
<a href="/download_pmc_report/{{ contInfo.PMC_No}}">
<button class="download-btn">Download PMC Report</button>
</a>
</body>
{% endblock %}

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %}
{% block content %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -7,102 +8,104 @@
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/report.css') }}">
<script src="{{ url_for('static', filename='js/searchContractor.js') }}"></script>
</head>
<body>
<div id="report" class="page">
<h2>Search Contractor Report</h2>
<div>
<form id="search-form">
<div class="row2">
<div>
<label for="subcontractor_name">Subcontractor Name:</label>
<input type="text" id="subcontractor_name" name="subcontractor_name">
<div id="report" class="page">
<h2>Search Contractor Report</h2>
<div>
<form id="search-form">
<div class="row2">
<div>
<label for="subcontractor_name">Subcontractor Name:</label>
<input type="text" id="subcontractor_name" name="subcontractor_name">
</div>
<div>
<label for="pmc_no">PMC No:</label>
<input type="text" id="pmc_no" name="pmc_no">
</div>
</div>
<div>
<label for="pmc_no">PMC No:</label>
<input type="text" id="pmc_no" name="pmc_no">
<div class="row2">
<div>
<label for="state">State:</label>
<input type="text" id="state" name="state">
</div>
<div>
<label for="district">District:</label>
<input type="text" id="district" name="district">
</div>
</div>
</div>
<div class="row2">
<div>
<label for="state">State:</label>
<input type="text" id="state" name="state">
<div class="row2">
<div>
<label for="block">Block:</label>
<input type="text" id="block" name="block">
</div>
<div>
<label for="village">Village:</label>
<input type="text" id="village" name="village">
</div>
</div>
<div>
<label for="district">District:</label>
<input type="text" id="district" name="district">
<div class="row2">
<div>
<label for="year_from">Year From:</label>
<input type="date" id="year_from" name="year_from">
</div>
<div>
<label for="year_to">Year To:</label>
<input type="date" id="year_to" name="year_to">
</div>
</div>
</div>
<div class="row2">
<div>
<label for="block">Block:</label>
<input type="text" id="block" name="block">
</div>
<div>
<label for="village">Village:</label>
<input type="text" id="village" name="village">
</div>
</div>
<div class="row2">
<div>
<label for="year_from">Year From:</label>
<input type="date" id="year_from" name="year_from">
</div>
<div>
<label for="year_to">Year To:</label>
<input type="date" id="year_to" name="year_to">
</div>
</div>
</form>
<button type="button" onclick="fetchData()">Search</button>
</form>
<h2>Contractor List</h2>
<table border="1" id="result-table">
<thead>
<tr>
<th class="sortable">Contractor Name
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th>PMC No</th>
<th class="sortable">State
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">District
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">Block
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">Village
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
</tr>
</thead>
<tbody>
<!-- Results will be dynamically populated -->
</tbody>
</table>
<h2>Contractor List</h2>
<table border="1" id="result-table">
<thead>
<tr>
<th class="sortable">Contractor Name
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th>PMC No</th>
<th class="sortable">State
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">District
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">Block
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
<th class="sortable">Village
<select>
<option value="">🔽</option>
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</th>
</tr>
</thead>
<tbody>
<!-- Results will be dynamically populated -->
</tbody>
</table>
</div>
</div>
</div>
</body>
{% endblock %}

View File

@@ -58,8 +58,9 @@
<!-- Hidden fields for other necessary information -->
<input type="text" name="subcontractor_data" value="{{ subcontractor_data['Contractor_Id'] }}">
<input type="text" name="state_data" value="{{ state_data['State_ID'] }}">
<input type="text" name="district_data" value="{{ district_data['District_ID'] }}">
<input type="text" name="state_data" value="{{ state_data['State_Id'] }}">
<!-- <input type="text" name="district_data" value="{{ district_data['District_ID'] }}"> -->
<input type="text" name="district_data" value="{{ district_data['District_id'] }}">
<input type="text" name="block_data" value="{{ block_data['Block_Id'] }}">
<input type="text" name="file_info" value="{{ file_info }}">

View File

@@ -1,8 +1,6 @@
{% extends 'base.html' %}
{% block content %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -10,363 +8,345 @@
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">-->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/subcontractor_report.css') }}">
</head>
<body>
<div class="container">
<h2>Contractor Report</h2>
<div class="container">
<h2>Contractor Report</h2>
<div class="info">
<h2>Contractor Details</h2>
<div class="row2">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" value="{{ contInfo.Contractor_Name }}"
readonly/>
<div class="info">
<h2>Contractor Details</h2>
<div class="row2">
<div>
<label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" value="{{ contInfo.Contractor_Name }}"
readonly />
</div>
<div>
<label for="Address">Address :</label>
<textarea id="Address" name="Address" readonly>{{ contInfo.Address }}</textarea>
</div>
</div>
<div>
<label for="Address">Address :</label>
<textarea id="Address" name="Address" readonly>{{ contInfo.Address }}</textarea>
<div class="row3">
<div>
<label for="PAN_No">PAN No :</label>
<input type="text" id="PAN_No" name="PAN_No" value="{{ contInfo.PAN_No }}" readonly />
</div>
<div>
<label for="Mobile_No">Mobile Number :</label>
<input type="text" id="Mobile_No" name="Mobile_No" value="{{ contInfo.Mobile_No }}" readonly />
</div>
<div>
<label for="Email">Email :</label>
<input type="text" id="Email" name="Email" value="{{ contInfo.Email }}" readonly />
</div>
</div>
</div>
<div class="row3">
<div>
<label for="PAN_No">PAN No :</label>
<input type="text" id="PAN_No" name="PAN_No" value="{{ contInfo.PAN_No }}" readonly/>
</div>
<div>
<label for="Mobile_No">Mobile Number :</label>
<input type="text" id="Mobile_No" name="Mobile_No" value="{{ contInfo.Mobile_No }}" readonly/>
</div>
<div>
<label for="Email">Email :</label>
<input type="text" id="Email" name="Email" value="{{ contInfo.Email }}" readonly/>
</div>
</div>
<div class="row2">
<div>
<label for="GST_Registration_Type">GST Registration Type :</label>
<input type="text" id="GST_Registration_Type" name="GST_Registration_Type"
value="{{ contInfo.GST_Registration_Type }}" readonly/>
<div class="row2">
<div>
<label for="GST_Registration_Type">GST Registration Type :</label>
<input type="text" id="GST_Registration_Type" name="GST_Registration_Type"
value="{{ contInfo.GST_Registration_Type }}" readonly />
</div>
<div>
<label for="GST_No">GST No:</label>
<input type="text" id="GST_No" name="GST_No" value="{{ contInfo.GST_No }}" readonly />
</div>
</div>
<div>
<label for="GST_No">GST No:</label>
<input type="text" id="GST_No" name="GST_No" value="{{ contInfo.GST_No }}" readonly/>
<div class="row3">
<div>
<label for="State">State :</label>
<input type="text" id="State" name="State" value="{{ contInfo.State_Name }}" readonly />
</div>
<div>
<label for="District">District :</label>
<input type="text" id="District" name="District" value="{{ contInfo.District_Name }}" readonly />
</div>
<div>
<label for="Block">Block :</label>
<input type="text" id="Block" name="Block" value="{{ contInfo.Block_Name }}" readonly />
</div>
</div>
</div>
<div class="row3">
<div>
<label for="State">State :</label>
<input type="text" id="State" name="State" value="{{ contInfo.State_Name }}" readonly/>
</div>
<div>
<label for="District">District :</label>
<input type="text" id="District" name="District" value="{{ contInfo.District_Name }}" readonly/>
</div>
<div>
<label for="Block">Block :</label>
<input type="text" id="Block" name="Block" value="{{ contInfo.Block_Name }}" readonly/>
</div>
</div>
</div>
<h3>Total</h3>
<table class="total-table">
<tr>
<th colspan="2">{{current_date}}</th>
</tr>
<!-- <tr>-->
<!-- <th>Total Hold Amounnt</th>-->
<!-- <td>0</td>-->
<!-- </tr>-->
<tr>
<th>Advance / Suplus</th>
<td>{{total["sum_invo_final_amt"]+total["sum_gst_final_amt"]-total["sum_pay_total_amt"]}}</td>
</tr>
{% if hold_types %}
<tr>
<!-- {% if hold_types %}
<h3>Hold Types</h3>
<ul>
{% for hold in hold_types %}
<th>{{ hold.hold_type }}</th>
<td>{{total["sum_invo_hold_amt"]}}</td>
<li>{{ hold.hold_type }}</li>
{% endfor %}
</tr>
{% endif %}
<tr>
<th>Amount With TDS</th>
<td>{{total["sum_invo_tds_amt"]}}</td>
</tr>
</table>
<!-- {% if hold_types %}-->
<!-- <h3>Hold Types</h3>-->
<!-- <ul>-->
<!-- {% for hold in hold_types %}-->
<!-- <li>{{ hold.hold_type }}</li>-->
<!-- {% endfor %}-->
<!-- </ul>-->
<!-- {% endif %}-->
</ul>
{% endif %} -->
<h3>Invoice Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Village Name</th>
<th>Work Type</th>
<th>Invoice Details</th>
<th>Invoice Date</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST (18%)</th>
<th>Amount</th>
<th>TDS (1%)</th>
<th>SD (5%)</th>
<th>On Commission</th>
<th>Hydro Testing</th>
<h3>Invoice Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Village Name</th>
<th>Work Type</th>
<th>Invoice Details</th>
<th>Invoice Date</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST (18%)</th>
<th>Amount</th>
<th>TDS (1%)</th>
<th>SD (5%)</th>
<th>On Commission</th>
<th>Hydro Testing</th>
<!-- Dynamic Hold Types -->
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{ hold }}</th>
{% endfor %}
<!-- Dynamic Hold Types -->
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{ hold }}</th>
{% endfor %}
<th>GST SD (18%)</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for invoice in invoices %}
<tr>
<td>{{ invoice.PMC_No }}</td>
<td>{{ invoice.Village_Name.capitalize() }}</td>
<td>{{ invoice.Work_Type }}</td>
<td>{{ invoice.Invoice_Details }}</td>
<td>{{ invoice.Invoice_Date }}</td>
<td>{{ invoice.Invoice_No }}</td>
<td>{{ invoice.Basic_Amount }}</td>
<td>{{ invoice.Debit_Amount }}</td>
<td>{{ invoice.After_Debit_Amount }}</td>
<td>{{ invoice.GST_Amount }}</td>
<td>{{ invoice.Amount }}</td>
<td>{{ invoice.TDS_Amount }}</td>
<td>{{ invoice.SD_Amount }}</td>
<td>{{ invoice.On_Commission }}</td>
<td>{{ invoice.Hydro_Testing }}</td>
<th>GST SD (18%)</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for invoice in invoices %}
<tr>
<td>{{ invoice.PMC_No }}</td>
<td>{{ invoice.Village_Name}}</td>
<td>{{ invoice.Work_Type }}</td>
<td>{{ invoice.Invoice_Details }}</td>
<td>{{ invoice.Invoice_Date }}</td>
<td>{{ invoice.invoice_no }}</td>
<td>{{ invoice.Basic_Amount }}</td>
<td>{{ invoice.Debit_Amount }}</td>
<td>{{ invoice.After_Debit_Amount }}</td>
<td>{{ invoice.GST_Amount }}</td>
<td>{{ invoice.Amount }}</td>
<td>{{ invoice.TDS_Amount }}</td>
<td>{{ invoice.SD_Amount }}</td>
<td>{{ invoice.On_Commission }}</td>
<td>{{ invoice.Hydro_Testing }}</td>
<!-- Dynamic Hold Amounts -->
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<td>
{% if invoice.hold_type == hold %}
{{ invoice.hold_amount }}
{% else %}
0.00
{% endif %}
</td>
<td>hold.</td>
{% endfor %}
<td>{{ invoice.GST_SD_Amount }}</td>
<td>{{ invoice.Final_Amount }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="6">Total</th>
<th>{{total["sum_invo_basic_amt"]}}</th>
<th>{{total["sum_invo_debit_amt"]}}</th>
<th>{{total["sum_invo_after_debit_amt"]}}</th>
<th>{{total["sum_invo_gst_amt"]}}</th>
<th>{{total["sum_invo_amt"]}}</th>
<th>{{total["sum_invo_tds_amt"]}}</th>
<th>{{total["sum_invo_ds_amt"]}}</th>
<th>{{total["sum_invo_on_commission"]}}</th>
<th>{{total["sum_invo_hydro_test"]}}</th>
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{total["sum_invo_hold_amt"]}}</th>
{% endfor %}
<th>{{total["sum_invo_gst_sd_amt"]}}</th>
<th>{{total["sum_invo_final_amt"]}}</th>
</tr>
<!-- Dynamic Hold Amounts -->
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<td>
{% if invoice.hold_type == hold %}
{{ invoice.hold_amount }}
{% else %}
0.00
<tr>
<td colspan="{{ 17 + hold_types|length }}">No invoices found.</td>
</tr>
{% endif %}
</td>
{% endfor %}
</tbody>
</table>
<td>{{ invoice.GST_SD_Amount }}</td>
<td>{{ invoice.Final_Amount }}</td>
</tr>
<h3>Hold Release</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if hold_release %}
{% for hold in hold_release %}
<tr>
<td>{{ hold['PMC_No'] }}</td>
<td>{{ hold['Invoice_No'] }}</td>
<td>{{ hold['Invoice_Details'] }}</td>
<td>{{ hold['Basic_Amount'] }}</td>
<td>{{ hold['Total_Amount'] }}</td>
<td>{{ hold['UTR'] }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="6" style="text-align:center;">No data present</td>
</tr>
{% endif %}
</tbody>
</table>
<br>
{% endfor %}
<tr>
<th colspan="6">Total</th>
<th>{{total["sum_invo_basic_amt"]}}</th>
<th>{{total["sum_invo_debit_amt"]}}</th>
<th>{{total["sum_invo_after_debit_amt"]}}</th>
<th>{{total["sum_invo_gst_amt"]}}</th>
<th>{{total["sum_invo_amt"]}}</th>
<th>{{total["sum_invo_tds_amt"]}}</th>
<th>{{total["sum_invo_ds_amt"]}}</th>
<th>{{total["sum_invo_on_commission"]}}</th>
<th>{{total["sum_invo_hydro_test"]}}</th>
{% set hold_types = invoices | map(attribute='hold_type') | unique | list %}
{% for hold in hold_types %}
<th>{{total["sum_invo_hold_amt"]}}</th>
{% endfor %}
<th>{{total["sum_invo_gst_sd_amt"]}}</th>
<th>{{total["sum_invo_final_amt"]}}</th>
<h3>Credit Details</h3>
</tr>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST Amount</th>
<th>Amount</th>
<th>Final Amount</th>
<th>Payment Amount</th>
<th>Total Amount</th>
<th>UTR</th>
{% else %}
<tr>
<td colspan="{{ 17 + hold_types|length }}">No invoices found.</td>
</tr>
{% endif %}
</tbody>
</table>
</tr>
<h3>Hold Release</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if hold_release %}
{% for hold in hold_release %}
<tr>
<td>{{ hold['PMC_No'] }}</td>
<td>{{ hold['Invoice_No'] }}</td>
<td>{{ hold['Invoice_Details'] }}</td>
<td>{{ hold['Basic_Amount'] }}</td>
<td>{{ hold['Total_Amount'] }}</td>
<td>{{ hold['UTR'] }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="6" style="text-align:center;">No data present</td>
</tr>
{% endif %}
</tbody>
</table>
<br>
{% if credit_note %}
{% for credit in credit_note %}
<tr>
<td>{{ credit["PMC_No"] }}</td>
<td>{{ credit["Invoice_Details"] }}</td>
<td>{{ credit["Basic_Amount"] }}</td>
<td>{{ credit["Debit_Amount"] }}</td>
<td>{{ credit["After_Debit_Amount"] }}</td>
<td>{{ credit["GST_Amount"] }}</td>
<td>{{ credit["Amount"] }}</td>
<td>{{ credit["Final_Amount"] }}</td>
<td>{{ credit["Payment_Amount"] }}</td>
<td>{{ credit["Total_Amount"] }}</td>
<td>{{ credit["UTR"] }}</td>
<tr>
<h3>Credit Details</h3>
</tr>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice Details</th>
<th>Basic Amount</th>
<th>Debit</th>
<th>After Debit Amt</th>
<th>GST Amount</th>
<th>Amount</th>
<th>Final Amount</th>
<th>Payment Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
{% endfor %}
</tr>
{% if credit_note %}
{% for credit in credit_note %}
<tr>
<td>{{ credit["PMC_No"] }}</td>
<td>{{ credit["Invoice_Details"] }}</td>
<td>{{ credit["Basic_Amount"] }}</td>
<td>{{ credit["Debit_Amount"] }}</td>
<td>{{ credit["After_Debit_Amount"] }}</td>
<td>{{ credit["GST_Amount"] }}</td>
<td>{{ credit["Amount"] }}</td>
<td>{{ credit["Final_Amount"] }}</td>
<td>{{ credit["Payment_Amount"] }}</td>
<td>{{ credit["Total_Amount"] }}</td>
<td>{{ credit["UTR"] }}</td>
{% else %}
<tr>
<td colspan="11">No Credit note found.</td>
</tr>
{% endif %}
</tr>
{% endfor %}
</thead>
</table>
{% else %}
<tr>
<td colspan="11">No Credit note found.</td>
</tr>
{% endif %}
<h3>GST Release Note Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Final Amount</th>
<th>Total Amount</th>
<th>UTR</th>
</tr>
</thead>
</table>
<h3>GST Release Note Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Basic Amount</th>
<th>Final Amount</th>
</tr>
</thead>
<tbody>
{% if gst_rel %}
{% for gst in gst_rel %}
<tr>
<td>{{ gst.pmc_no }}</td>
<td>{{ gst.Invoice_No }}</td>
<td>{{ gst.Basic_Amount }}</td>
<td>{{ gst.Final_Amount }}</td>
<td>{{ gst.Total_Amount }}</td>
<td>{{ gst.UTR }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_gst_basic_amt"]}}</th>
<th>{{total["sum_gst_final_amt"]}}</th>
</tr>
{% else %}
<tr>
<td colspan="4">No GST release found.</td>
</tr>
{% endif %}
</tbody>
</table>
</thead>
<tbody>
{% if gst_rel %}
{% for gst in gst_rel %}
<tr>
<td>{{ gst.pmc_no }}</td>
<td>{{ gst.invoice_no }}</td>
<td>{{ gst.basic_amount }}</td>
<td>{{ gst.final_amount }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<br>
<h3>Payment Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Amount</th>
<th>TDS Amount @ 1% on BASIC AMOUNT</th>
<th>Total Amount Paid</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if invoices %}
{% for pay in invoices %}
<tr>
<td>{{ pay.PMC_No }}</td>
<td>{{ pay.invoice_no }}</td>
<td>{{ pay.Payment_Amount }}</td>
<td>{{ pay.TDS_Payment_Amount }}</td>
<td>{{ pay.Total_Amount }}</td>
<td>{{ pay.UTR}}</td>
</tr>
{% endfor %}
<th>{{total["sum_gst_basic_amt"]}}</th>
<th>{{total["sum_gst_final_amt"]}}</th>
</tr>
{% else %}
<tr>
<td colspan="4">No GST release found.</td>
</tr>
{% endif %}
</tbody>
</table>
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_pay_payment_amt"]}}</th>
<th>{{total["sum_pay_tds_payment_amt"]}}</th>
<th>{{total["sum_pay_total_amt"]}}</th>
<th></th>
</tr>
{% else %}
<tr>
<td colspan="6">No payment found.</td>
</tr>
{% endif %}
</tbody>
</table>
<br>
<h3>Payment Details</h3>
<table>
<thead>
<tr>
<th>PMC No</th>
<th>Invoice No</th>
<th>Amount</th>
<th>TDS Amount @ 1% on BASIC AMOUNT</th>
<th>Total Amount Paid</th>
<th>UTR</th>
</tr>
</thead>
<tbody>
{% if payments %}
{% for pay in payments %}
<tr>
<td>{{ pay.pmc_no }}</td>
<td>{{ pay.invoice_no }}</td>
<td>{{ pay.Payment_Amount }}</td>
<td>{{ pay.TDS_Payment_Amount }}</td>
<td>{{ pay.Total_amount }}</td>
<td>{{ pay.utr}}</td>
</tr>
{% endfor %}
<tr>
<th colspan="2">Total</th>
<th>{{total["sum_pay_payment_amt"]}}</th>
<th>{{total["sum_pay_tds_payment_amt"]}}</th>
<th>{{total["sum_pay_total_amt"]}}</th>
<th></th>
</tr>
{% else %}
<tr>
<td colspan="6">No payment found.</td>
</tr>
{% endif %}
</tbody>
</table>
<a href="{{ url_for('report.download_report', contractor_id=contractor_id) }}" class="download-btn">Download Report</a>
</div>
<a href="{{ url_for('report.download_report', contractor_id=contractor_id) }}" class="download-btn">Download
Report</a>
</div>
</body>
{% endblock %}