Files
PaymentRec/main.py
2025-06-19 23:04:49 +05:30

7454 lines
326 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from decimal import Decimal
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
import mysql.connector
from mysql.connector import Error
import config
import openpyxl
import os
import re
import ast
from datetime import datetime
import pandas as pd
from openpyxl.styles import Font
# this is server
app = Flask(__name__)
app.secret_key = '9f2a1b8c4d6e7f0123456789abcdef01'
str_pattern_reg = "^[A-Za-z ]+$"
class ResponseHandler:
@staticmethod
def invalid_name(entity):
return {'status': 'error', 'message': f'Invalid {entity} name. Only letters are allowed!'}
@staticmethod
def already_exists(entity):
return {'status': 'exists', 'message': f'{entity.capitalize()} already exists!'}
@staticmethod
def add_success(entity):
return {'status': 'success', 'message': f'{entity.capitalize()} added successfully!'}
@staticmethod
def add_failure(entity):
return {'status': 'error', 'message': f'Failed to add {entity}.'}
@staticmethod
def is_available(entity):
return {'status': 'available', 'message': f'{entity.capitalize()} name is available!'}
@staticmethod
def delete_success(entity):
return {'status': 'success', 'message': f'{entity.capitalize()} deleted successfully!'}
@staticmethod
def delete_failure(entity):
return {'status': 'error', 'message': f'Failed to delete {entity}.'}
@staticmethod
def update_success(entity):
return {'status': 'success', 'message': f'{entity.capitalize()} updated successfully!'}
@staticmethod
def update_failure(entity):
return {'status': 'error', 'message': f'Failed to update {entity}.'}
@staticmethod
def fetch_failure(entity):
return f"Failed to fetch {entity}"
# Helper: JSON Response Formatter
def json_response(message_obj, status_code):
return jsonify(message_obj), status_code
# this is Index page OR Home page..
@app.route('/')
def index():
return render_template('index.html')
# this is Profile page ..
@app.route('/admin_profile', methods=['GET', 'POST'])
def admin_profile():
return render_template('admin_profile.html')
# ------------------------- State controller ------------------------------------------
@app.route('/add_state', methods=['GET', 'POST'])
def add_state():
connection = config.get_db_connection()
statedata = []
if connection:
cursor = connection.cursor()
if request.method == 'POST':
state_name = request.form['state_Name'].strip()
if not re.match(str_pattern_reg, state_name):
return json_response(ResponseHandler.invalid_name("state"), 400)
try:
# cursor.execute("SELECT * FROM states WHERE State_Name = %s", (state_name,))
# if cursor.fetchone():
# return json_response(ResponseHandler.already_exists("state"), 409)
cursor.callproc("CheckStateExists", (state_name,))
for data in cursor.stored_results():
existing_state = data.fetchone()
if existing_state:
return json_response(ResponseHandler.already_exists("state"), 409)
# cursor.execute("call SaveState (%s)", (state_name,))
cursor.callproc("SaveState", (state_name,))
connection.commit()
return json_response(ResponseHandler.add_success("state"), 200)
except mysql.connector.Error as e:
print(f"Error inserting state: {e}")
return json_response(ResponseHandler.add_failure("state"), 500)
try:
# cursor.execute("SELECT State_ID, State_Name FROM states")
# statedata = cursor.fetchall()
cursor.callproc("GetAllStates")
for res in cursor.stored_results():
statedata = res.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching states: {e}")
return ResponseHandler.fetch_failure("states"), 500
finally:
cursor.close()
connection.close()
return render_template('add_state.html', statedata=statedata)
# AJAX route to check state existence
@app.route('/check_state', methods=['POST'])
def check_state():
connection = config.get_db_connection()
if connection:
cursor = connection.cursor()
state_name = request.json.get('state_Name', '').strip()
if not re.match(str_pattern_reg, state_name):
return json_response(ResponseHandler.invalid_name("state"), 400)
try:
# cursor.execute("SELECT * FROM states WHERE State_Name = %s", (state_name,))
# existing_state = cursor.fetchone()
cursor.callproc("CheckStateExists", (state_name,))
for data in cursor.stored_results():
existing_state = data.fetchone()
if existing_state:
return json_response(ResponseHandler.already_exists("state"), 409)
else:
return json_response(ResponseHandler.is_available("state"), 200)
except mysql.connector.Error as e:
print(f"Error checking state: {e}")
return json_response(ResponseHandler.add_failure("state"), 500)
finally:
cursor.close()
connection.close()
# Delete State
@app.route('/delete_state/<int:id>', methods=['GET'])
def deleteState(id):
connection = config.get_db_connection()
cursor = connection.cursor()
try:
# cursor.execute("DELETE FROM states WHERE State_ID = %s", (id,))
cursor.callproc('DeleteState', (id,))
connection.commit()
# For API response
# return json_response(ResponseHandler.delete_success("state"), 200)
except mysql.connector.Error as e:
print(f"Error deleting data: {e}")
return json_response(ResponseHandler.delete_failure("state"), 500)
finally:
cursor.close()
connection.close()
return redirect(url_for('add_state'))
# Edit State
@app.route('/edit_state/<int:id>', methods=['GET', 'POST'])
def editState(id):
connection = config.get_db_connection()
cursor = connection.cursor()
# str_pattern_reg = r"^[A-Za-z\s]+$"
if request.method == 'POST':
state_name = request.form['state_Name'].strip()
if not re.match(str_pattern_reg, state_name):
return ResponseHandler.invalid_name("state"), 400
try:
# cursor.execute("UPDATE states SET State_Name = %s WHERE State_ID = %s", (state_name, id))
cursor.callproc("UpdateStateById", (id, state_name))
connection.commit()
return redirect(url_for('add_state'))
except mysql.connector.Error as e:
print(f"Error updating data: {e}")
return ResponseHandler.add_failure("state"), 500
finally:
cursor.close()
connection.close()
try:
# cursor.execute("SELECT * FROM states WHERE State_ID = %s", (id,))
# state = cursor.fetchone()
cursor.callproc("GetStateByID", (id,))
for result in cursor.stored_results():
state = result.fetchone()
if state is None:
return "State not found", 404
except mysql.connector.Error as e:
print(f"Error retrieving data: {e}")
return ResponseHandler.fetch_failure("state"), 500
finally:
cursor.close()
connection.close()
return render_template('edit_state.html', state=state)
# -------- end State controller -----------
# ------------------------- District controller ------------------------------------------
@app.route('/add_district', methods=['GET', 'POST'])
def add_district():
connection = config.get_db_connection()
districtdata = []
states = []
if connection:
cursor = connection.cursor()
try:
# cursor.execute("SELECT State_ID, State_Name FROM states")
# states = cursor.fetchall()
cursor.callproc("GetAllStates")
for res in cursor.stored_results():
states = res.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching states: {e}")
return ResponseHandler.fetch_failure("states"), 500
if request.method == 'POST':
district_name = request.form['district_Name'].strip()
state_id = request.form['state_Id']
if not re.match(str_pattern_reg, district_name):
return json_response(ResponseHandler.invalid_name("district"), 400)
try:
# cursor.execute("SELECT * FROM districts WHERE District_Name = %s AND State_Id = %s",
# (district_name, state_id))
cursor.callproc("GetDistrictByNameAndState", (district_name, state_id))
for data in cursor.stored_results():
rs = data.fetchone()
if rs:
return json_response(ResponseHandler.already_exists("district"), 409)
cursor.callproc('SaveDistrict', (district_name, state_id))
connection.commit()
return json_response(ResponseHandler.add_success("district"), 200)
except mysql.connector.Error as e:
print(f"Error inserting district: {e}")
return json_response(ResponseHandler.add_failure("district"), 500)
try:
# cursor.execute("SELECT d.District_id, d.District_Name, s.State_Name, s.State_Id FROM districts d JOIN states s ON d.State_Id = s.State_ID")
# districtdata = cursor.fetchall()
cursor.callproc("GetAllDistricts")
for dis in cursor.stored_results():
districtdata = dis.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching districts: {e}")
return ResponseHandler.fetch_failure("districts"), 500
finally:
cursor.close()
connection.close()
return render_template('add_district.html', districtdata=districtdata, states=states)
# AJAX route to check district existence
@app.route('/check_district', methods=['POST'])
def check_district():
connection = config.get_db_connection()
if connection:
cursor = connection.cursor()
district_name = request.json.get('district_Name', '').strip()
state_id = request.json.get('state_Id', '')
if not re.match(str_pattern_reg, district_name):
return json_response(ResponseHandler.invalid_name("district"), 400)
try:
# cursor.execute("SELECT * FROM districts WHERE District_Name = %s AND State_Id = %s",
# (district_name, state_id))
# existing_district = cursor.fetchone()
cursor.callproc("GetDistrictByNameAndState", (district_name, state_id,))
for result in cursor.stored_results():
existing_district = result.fetchone()
if existing_district:
return json_response(ResponseHandler.already_exists("district"), 409)
else:
return json_response(ResponseHandler.is_available("district"), 200)
except mysql.connector.Error as e:
print(f"Error checking district: {e}")
return json_response(ResponseHandler.add_failure("district"), 500)
finally:
cursor.close()
connection.close()
# this is delete District method by id..
@app.route('/delete_district/<int:district_id>', methods=['GET', 'POST'])
def delete_district(district_id):
connection = config.get_db_connection()
if connection:
cursor = connection.cursor()
try:
# cursor.execute("DELETE FROM districts WHERE District_id = %s", (district_id,))
cursor.callproc("DeleteDistrict", (district_id,))
connection.commit()
except mysql.connector.Error as e:
print(f"Error deleting district: {e}")
return json_response(ResponseHandler.delete_failure("district"), 500)
finally:
cursor.close()
connection.close()
return redirect('/add_district')
# this is update District page by id ..
@app.route('/edit_district/<int:district_id>', methods=['GET', 'POST'])
def edit_district(district_id):
connection = config.get_db_connection()
districtdata = []
states = []
if connection:
cursor = connection.cursor()
# Retrieve all states for dropdown
try:
# cursor.execute("SELECT State_ID, State_Name FROM states")
# states = cursor.fetchall()
cursor.callproc("GetAllStates")
for res in cursor.stored_results():
states = res.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching states: {e}")
return ResponseHandler.fetch_failure("states"), 500
# Retrieve district info
try:
# cursor.execute("SELECT District_Name, State_Id FROM districts WHERE District_id = %s", (district_id,))
# districtdata = cursor.fetchone()
cursor.callproc("GetDistrictDataByID", (district_id,))
for rs in cursor.stored_results():
districtdata = rs.fetchone()
except mysql.connector.Error as e:
print(f"Error fetching district data: {e}")
return ResponseHandler.fetch_failure("district"), 500
# Handle update
if request.method == 'POST':
district_name = request.form['district_Name']
state_id = request.form['state_Id']
try:
# cursor.execute( "UPDATE districts SET District_Name = %s, State_Id = %s WHERE District_id = %s",
# (district_name, state_id, district_id) )
cursor.callproc("UpdateDistrict", (district_id, state_id, district_name,))
connection.commit()
except mysql.connector.Error as e:
print(f"Error updating district: {e}")
return ResponseHandler.update_failure("district"), 500
return redirect('/add_district')
return render_template('edit_district.html', districtdata=districtdata, states=states)
# --------- end District controller -------------
# ------------------------- Block controller ------------------------------------------
@app.route('/add_block', methods=['GET', 'POST'])
def add_block():
connection = config.get_db_connection()
block_data = []
states = []
if connection:
cursor = connection.cursor()
try:
# cursor.execute("SELECT State_ID, State_Name FROM states")
# states = cursor.fetchall()
cursor.callproc("GetAllStates")
for res in cursor.stored_results():
states = res.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching states: {e}")
return json_response(ResponseHandler.fetch_failure("states"), 500)
if request.method == 'POST':
block_name = request.form['block_Name'].strip()
district_id = request.form['district_Id']
if not re.match(str_pattern_reg, block_name):
return json_response(ResponseHandler.invalid_name("block"), 400)
try:
# cursor.execute("SELECT * FROM blocks WHERE Block_Name = %s AND District_id = %s",
# (block_name, district_id))
# existing_block = cursor.fetchone()
cursor.callproc("GetBlockByNameAndDistrict", (block_name, district_id,))
for rs in cursor.stored_results():
existing_block = rs.fetchone()
if existing_block:
return json_response(ResponseHandler.already_exists("block"), 409)
cursor.callproc('SaveBlock', (block_name, district_id))
connection.commit()
return json_response(ResponseHandler.add_success("block"), 200)
except mysql.connector.Error as e:
print(f"Error adding block: {e}")
return json_response(ResponseHandler.add_failure("block"), 500)
# Fetch all blocks to display
try:
# cursor.execute(
# """SELECT b.Block_Id, b.Block_Name, d.District_Name
# FROM blocks b
# JOIN districts d ON b.District_id = d.District_id"""
# )
# block_data = cursor.fetchall()
cursor.callproc("GetAllBlocks")
for blocks in cursor.stored_results():
block_data = blocks.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching blocks: {e}")
return json_response(ResponseHandler.fetch_failure("blocks"), 500)
finally:
cursor.close()
connection.close()
return render_template('add_block.html', block_data=block_data, states=states)
# check block
@app.route('/check_block', methods=['POST'])
def check_block():
connection = config.get_db_connection()
cursor = connection.cursor()
block_name = request.json.get('block_Name', '').strip()
district_id = request.json.get('district_Id', '')
if not re.match(str_pattern_reg, block_name):
return json_response(ResponseHandler.invalid_name("block"), 400)
# cursor.execute("SELECT * FROM blocks WHERE Block_Name = %s AND District_id = %s", (block_name, district_id))
# existing_block = cursor.fetchone()
cursor.callproc("GetBlockByNameAndDistrict", (block_name, district_id))
for rs in cursor.stored_results():
existing_block = rs.fetchone()
if existing_block:
return json_response(ResponseHandler.already_exists("block"), 409)
return json_response(ResponseHandler.is_available("block"), 200)
# update block by id
# @app.route('/edit_block/<int:block_id>', methods=['GET', 'POST'])
# def edit_block(block_id):
# connection = config.get_db_connection()
# block_data, states, districts = [], [], []
#
# if connection:
# cursor = connection.cursor()
# try:
# # cursor.execute("SELECT State_ID, State_Name FROM states")
# # states = cursor.fetchall()
# cursor.callproc("GetAllStates")
# for res in cursor.stored_results():
# states = res.fetchall()
#
# # cursor.execute("SELECT Block_Name, District_id FROM blocks WHERE Block_Id = %s", (block_id,))
# # block_data = cursor.fetchone()
# cursor.callproc("GetBlockById",(block_id,))
# for block in cursor.stored_results():
# block_data = block.fetchone()
#
# except mysql.connector.Error as e:
# print(f"Error fetching block data: {e}")
# return json_response(ResponseHandler.fetch_failure("block data"), 500)
#
# if request.method == 'POST':
# block_name = request.form['block_Name']
# district_id = request.form['district_Id']
#
# try:
# # cursor.execute("UPDATE blocks SET Block_Name = %s, District_id = %s WHERE Block_Id = %s",
# # (block_name, district_id, block_id))
#
# cursor.callproc("UpdateBlock",(block_id,block_name, district_id))
# connection.commit()
# except mysql.connector.Error as e:
# print(f"Error updating block: {e}")
# return json_response(ResponseHandler.update_failure("block"), 500)
#
# return redirect('/add_block')
#
# try:
# # cursor.execute("SELECT District_id, District_Name FROM districts")
# # districts = cursor.fetchall()
#
# cursor.callproc("GetAllDistricts")
# for dis in cursor.stored_results():
# districts = dis.fetchall()
#
# except mysql.connector.Error as e:
# print(f"Error fetching districts: {e}")
# return json_response(ResponseHandler.fetch_failure("districts"), 500)
#
# return render_template('edit_block.html', block_data=block_data, states=states, districts=districts)
@app.route('/edit_block/<int:block_id>', methods=['GET', 'POST'])
def edit_block(block_id):
connection = config.get_db_connection()
block_data = []
states = []
districts = []
if connection:
cursor = connection.cursor()
# Retrieve all states
try:
# cursor.execute("SELECT State_ID, State_Name FROM states")
# states = cursor.fetchall()
cursor.callproc("GetAllStates")
for rs in cursor.stored_results():
states = rs.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching states: {e}")
return "Failed to fetch states", 500
# Retrieve block data
try:
# cursor.execute("SELECT Block_Name, District_id FROM blocks WHERE Block_Id = %s", (block_id,))
# block_data = cursor.fetchone()
cursor.callproc("GetBlockDataByID", (block_id,))
for rs in cursor.stored_results():
block_data = rs.fetchone()
except mysql.connector.Error as e:
print(f"Error fetching block data: {e}")
return "Failed to fetch block data", 500
# Handle POST request
if request.method == 'POST':
block_name = request.form['block_Name']
district_id = request.form['district_Id']
try:
# cursor.execute("UPDATE blocks SET Block_Name = %s, District_id = %s WHERE Block_Id = %s",
# (block_name, district_id, block_id))
cursor.callproc("UpdateBlockById", (block_name, district_id, block_id,))
connection.commit()
flash("Block updated successfully!", "success")
return redirect(url_for('add_block', block_id=block_id))
except mysql.connector.Error as e:
print(f"Error updating blocks: {e}")
return "Failed to update blocks", 500
# Retrieve districts for the dropdown
try:
# cursor.execute("SELECT District_id, District_Name FROM districts")
# districts = cursor.fetchall()
cursor.callproc("GetAllDistrictsData")
for rs in cursor.stored_results():
districts = rs.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching districts: {e}")
return "Failed to fetch districts", 500
return render_template('edit_block.html', block_data=block_data, states=states, districts=districts)
# delete block by id
@app.route('/delete_block/<int:block_id>', methods=['GET', 'POST'])
def delete_block(block_id):
connection = config.get_db_connection()
if connection:
cursor = connection.cursor()
try:
# cursor.execute("DELETE FROM blocks WHERE Block_Id = %s", (block_id,))
cursor.callproc("DeleteBlock", (block_id,))
connection.commit()
except mysql.connector.Error as e:
print(f"Error deleting block: {e}")
return json_response(ResponseHandler.add_failure("block"), 500)
finally:
cursor.close()
connection.close()
return redirect('/add_block')
# this is get district all data by using state id ..
@app.route('/get_districts/<int:state_id>', methods=['GET'])
def get_districts(state_id):
connection = config.get_db_connection()
districts = []
if connection:
cursor = connection.cursor()
try:
# cursor.execute("SELECT District_id, District_Name FROM districts WHERE State_Id = %s", (state_id,))
# districts = cursor.fetchall()
cursor.callproc("GetDistrictsByStateId", (state_id,))
for dis in cursor.stored_results():
districts = dis.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching districts: {e}")
return json_response(ResponseHandler.fetch_failure("districts"), 500)
finally:
cursor.close()
connection.close()
return jsonify({
"districts": [{"District_id": d[0], "District_Name": d[1]} for d in districts]
})
# ----------- end Block controller -----------------
# ------------------------- Village controller ------------------------------------------
# Route to add a village
@app.route('/add_village', methods=['GET', 'POST'])
def add_village():
connection = config.get_db_connection()
cursor = connection.cursor()
states = []
villages = []
try:
# Fetch all states
# cursor.execute("SELECT State_ID, State_Name FROM states")
# states = cursor.fetchall()
cursor.callproc("GetAllStates")
for res in cursor.stored_results():
states = res.fetchall()
# Fetch all villages with their block names
# cursor.execute("""
# SELECT v.Village_Id, v.Village_Name, b.Block_Name
# FROM villages v
# JOIN blocks b ON v.Block_Id = b.Block_Id
# """)
# villages = cursor.fetchall()
cursor.callproc("GetAllVillages")
for result in cursor.stored_results():
villages = result.fetchall()
if request.method == 'POST':
block_id = request.form.get('block_Id')
village_name = request.form.get('Village_Name', '').strip()
if not block_id:
return json_response(ResponseHandler.add_failure("block"), 400)
if not re.match(str_pattern_reg, village_name):
return json_response(ResponseHandler.invalid_name("village"), 400)
# Check if the village already exists in the block
# cursor.execute("SELECT * FROM villages WHERE Village_Name = %s AND Block_Id = %s", (village_name, block_id))
# existing_village = cursor.fetchone()
cursor.callproc("GetVillageByNameAndBlock", (village_name, block_id,))
for rs in cursor.stored_results():
existing_village = rs.fetchone()
if existing_village:
return json_response(ResponseHandler.already_exists("village"), 409)
# Insert new village
cursor.callproc('SaveVillage', (village_name, block_id))
connection.commit()
return json_response(ResponseHandler.add_success("village"), 200)
except mysql.connector.Error as e:
print(f"Database Error: {e}")
return json_response(ResponseHandler.add_failure("village"), 500)
finally:
cursor.close()
connection.close()
return render_template('add_village.html', states=states, villages=villages)
# get block by district id
@app.route('/get_blocks/<int:district_id>', methods=['GET'])
def get_blocks(district_id):
connection = config.get_db_connection()
cursor = connection.cursor()
blocks = []
try:
# cursor.execute("SELECT Block_Id, Block_Name FROM blocks WHERE District_id = %s", (district_id,))
# blocks = cursor.fetchall()
cursor.callproc("GetBlocksByDistrict", (district_id,))
for rs in cursor.stored_results():
blocks = rs.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching blocks: {e}")
return json_response({"error": "Failed to fetch blocks"}, 500)
finally:
cursor.close()
connection.close()
return jsonify({"blocks": [{"Block_Id": block[0], "Block_Name": block[1]} for block in blocks]})
# check village
@app.route('/check_village', methods=['POST'])
def check_village():
connection = config.get_db_connection()
cursor = connection.cursor()
block_id = request.form.get('block_Id')
village_name = request.form.get('Village_Name', '').strip()
# Validate village name
if not re.match(str_pattern_reg, village_name):
return json_response(ResponseHandler.invalid_name("village"), 400)
if not block_id or not village_name:
return json_response({'status': 'error', 'message': 'Block and Village Name are required!'}, 400)
# cursor.execute("SELECT * FROM villages WHERE Village_Name = %s AND Block_Id = %s", (village_name, block_id))
# existing_village = cursor.fetchone()
cursor.callproc("GetVillageByNameAndBlocks", (village_name, block_id))
for rs in cursor.stored_results():
existing_village = rs.fetchone()
cursor.close()
connection.close()
if existing_village:
return json_response(ResponseHandler.already_exists("village"), 409)
else:
return json_response(ResponseHandler.is_available("village"), 200)
# update village
# @app.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])
# def edit_village(village_id):
# connection = config.get_db_connection()
# village_data = None
# blocks = []
#
# try:
# cursor = connection.cursor()
# # Fetch village details
# # cursor.execute("SELECT Village_Name, Block_Id FROM villages WHERE Village_Id = %s", (village_id,))
# # village_data = cursor.fetchone()
# cursor.callproc("GetVillageById", (village_id,))
# for result in cursor.stored_results():
# village_data = result.fetchone()
#
# # Fetch all blocks for dropdown
# # cursor.execute("SELECT Block_Id, Block_Name FROM blocks")
# # blocks = cursor.fetchall()
#
# cursor.callproc("GetAllBlocks")
# for result in cursor.stored_results():
# blocks = result.fetchall()
#
# if request.method == 'POST':
# village_name = request.form['Village_Name']
# block_id = request.form['block_Id']
#
# if not re.match(str_pattern_reg, village_name):
# return json_response(ResponseHandler.invalid_name("village"), 400)
#
# # cursor.execute("UPDATE villages SET Village_Name = %s, Block_Id = %s WHERE Village_Id = %s",
# # (village_name, block_id, village_id))
#
# cursor.callproc("UpdateVillage", (village_id, village_name, block_id,))
#
# connection.commit()
# return json_response(ResponseHandler.update_success("village"), 200)
#
# except mysql.connector.Error as e:
# print(f"Error: {e}")
# return json_response(ResponseHandler.update_failure("village"), 500)
# finally:
# if cursor:
# cursor.close()
# if connection:
# connection.close()
#
# return render_template('edit_village.html', village_data=village_data, blocks=blocks)
@app.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])
def edit_village(village_id):
connection = config.get_db_connection()
village_data = None
blocks = []
try:
cursor = connection.cursor()
# # Fetch village details
# cursor.execute("SELECT Village_Name, Block_Id FROM villages WHERE Village_Id = %s", (village_id,))
# village_data = cursor.fetchone()
cursor.callproc("GetVillageDetailsById", (village_id,))
for rs in cursor.stored_results():
village_data = rs.fetchone()
# Fetch all blocks for dropdown
# cursor.execute("SELECT Block_Id, Block_Name FROM blocks")
# blocks = cursor.fetchall()
cursor.callproc('GetAllBlocks')
for result in cursor.stored_results():
blocks = result.fetchall()
if request.method == 'POST':
village_name = request.form['Village_Name']
block_id = request.form['block_Id']
if not re.match("^[A-Za-z ]+$", village_name):
flash("Invalid village name! Only letters and spaces allowed.", "error")
return redirect(url_for('edit_village', village_id=village_id))
cursor.execute("UPDATE villages SET Village_Name = %s, Block_Id = %s WHERE Village_Id = %s",
(village_name, block_id, village_id))
connection.commit()
flash("Village updated successfully!", "success")
return redirect(url_for('edit_village', village_id=village_id))
except mysql.connector.Error as e:
print(f"Error: {e}")
return "Failed to process request", 500
finally:
if cursor:
cursor.close()
if connection:
connection.close()
return render_template('edit_village.html', village_data=village_data, blocks=blocks)
# delete village
@app.route('/delete_village/<int:village_id>', methods=['GET', 'POST'])
def delete_village(village_id):
connection = config.get_db_connection()
cursor = connection.cursor()
try:
# cursor.execute("DELETE FROM villages WHERE Village_Id = %s", (village_id,))
cursor.callproc("DeleteVillage", (village_id,))
connection.commit()
# return json_response(ResponseHandler.delete_success("village"), 200)
except mysql.connector.Error as e:
print(f"Error: {e}")
return json_response(ResponseHandler.add_failure("village"), 500)
finally:
if cursor:
cursor.close()
if connection:
connection.close()
return redirect(url_for('add_village'))
# ---- end Village controller ---------------------
# -------------------------------- Invoice controller ------------------------------------------
@app.route('/add_invoice', methods=['GET', 'POST'])
def add_invoice():
connection = config.get_db_connection()
if not connection:
return jsonify({"status": "error", "message": "Database connection failed"}), 500
if request.method == 'POST':
try:
cursor = connection.cursor(dictionary=True)
# Get the village name from the form
village_name = request.form.get('village')
print("village name", village_name)
# Query the database to get the corresponding Village_Id based on the village name
# cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,))
# village_result = cursor.fetchone()
cursor.callproc("GetVillageIdByName", (village_name,))
for rs in cursor.stored_results():
village_result = rs.fetchone()
if not village_result:
return jsonify({"status": "error", "message": f"Village '{village_name}' not found"}), 400
village_id = village_result['Village_Id']
# Fetch form data
pmc_no = request.form.get('pmc_no')
work_type = request.form.get('work_type')
invoice_details = request.form.get('invoice_details')
invoice_date = request.form.get('invoice_date')
invoice_no = request.form.get('invoice_no')
basic_amount = request.form.get('basic_amount')
debit_amount = request.form.get('debit_amount')
after_debit_amount = request.form.get('after_debit_amount')
amount = request.form.get('amount')
gst_amount = request.form.get('gst_amount')
tds_amount = request.form.get('tds_amount')
sd_amount = request.form.get('sd_amount')
on_commission = request.form.get('on_commission')
hydro_testing = request.form.get('hydro_testing')
gst_sd_amount = request.form.get('gst_sd_amount')
final_amount = request.form.get('final_amount')
# insert_invoice_query = '''
# INSERT INTO invoice (
# PMC_No, Village_Id, Work_Type, Invoice_Details, Invoice_Date, Invoice_No,
# Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount,
# SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount
# ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
# '''
# invoice_values = (
# pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no,
# basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount,
# sd_amount, on_commission, hydro_testing, gst_sd_amount, final_amount
# )
# cursor.execute(insert_invoice_query, invoice_values)
# connection.commit()
# invoice_id = cursor.lastrowid
cursor.callproc('InsertInvoice', [
pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no,
basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount,
sd_amount, on_commission, hydro_testing, gst_sd_amount, final_amount])
for result in cursor.stored_results():
invoice_id = result.fetchone()['invoice_id']
connection.commit()
print("This is the invocie id from the invoice table ", invoice_id)
# Insert into assign_subcontractors table
# subcontractor_id = request.form.get('subcontractor_id')
# insert_assign_query = '''
# INSERT INTO assign_subcontractors (PMC_no, Contractor_Id, Village_Id)
# VALUES (%s, %s, %s)
# '''
# cursor.execute(insert_assign_query, (pmc_no, subcontractor_id, village_id))
# connection.commit()
subcontractor_id = request.form.get('subcontractor_id')
cursor.callproc('AssignSubcontractor', [pmc_no, subcontractor_id, village_id])
connection.commit()
# Insert Hold Amounts into invoice_subcontractor_hold_join table
hold_types = request.form.getlist('hold_type[]')
hold_amounts = request.form.getlist('hold_amount[]')
hold_count = 0
for hold_type, hold_amount in zip(hold_types, hold_amounts):
# cursor.execute("SELECT hold_type_id FROM hold_types WHERE hold_type = %s", (hold_type,))
# hold_type_result = cursor.fetchone()
cursor.callproc('GetHoldTypeIdByName', [hold_type])
for result in cursor.stored_results():
hold_type_result = result.fetchone()
print("hold type from invoice ", hold_type_result)
if not hold_type_result:
return jsonify({"status": "error", "message": f"Invalid Hold Type: {hold_type}"}), 400
hold_type_id = hold_type_result['hold_type_id']
# insert_hold_query = '''
# INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount)
# VALUES (%s, %s, %s, %s)
# '''
# cursor.execute(insert_hold_query, (subcontractor_id, invoice_id, hold_type_id, hold_amount))
# hold_count += 1
# connection.commit()
cursor.callproc('InsertInvoiceSubcontractorHold', [
subcontractor_id, invoice_id, hold_type_id, hold_amount
])
connection.commit()
hold_count += 1
print("Hold count from the invoice", hold_count)
connection.commit()
return jsonify({"status": "success", "message": "Invoice added successfully"}), 201
except mysql.connector.Error as e:
connection.rollback()
return jsonify({"status": "error", "message": f"Failed to add invoice: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
# GET request: fetch and display all invoices (all fields) along with the form
try:
cursor = connection.cursor(dictionary=True)
# cursor.execute("SELECT * FROM view_invoice_details")
# invoices = cursor.fetchall()
cursor.callproc('GetAllInvoiceDetails')
for result in cursor.stored_results():
invoices = result.fetchall()
villages = []
cursor.callproc("GetAllVillages")
for result in cursor.stored_results():
villages = result.fetchall()
except mysql.connector.Error as e:
print(f"Error: {e}")
invoices = []
finally:
cursor.close()
connection.close()
return render_template('add_invoice.html', invoices=invoices, villages=villages)
# search subcontraactor to assing invoice
@app.route('/search_subcontractor', methods=['POST'])
def search_subcontractor():
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("database connection"), 500)
sub_query = request.form.get("query")
try:
cursor = connection.cursor(dictionary=True)
# cursor.execute(
# "SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name LIKE %s",
# (f"%{sub_query}%",)
# )
# results = cursor.fetchall()
cursor.callproc('SearchContractorsByName', [sub_query])
for result in cursor.stored_results():
results = result.fetchall()
print(results)
if not results:
return "<li>No subcontractor found</li>"
output = "".join(
f"<li data-id='{row['Contractor_Id']}'>{row['Contractor_Name']}</li>"
for row in results
)
print("Ajax Call for subcontractor", output)
return output
except mysql.connector.Error as e:
return json_response(ResponseHandler.fetch_failure(f"Search failed: {str(e)}"), 500)
finally:
cursor.close()
connection.close()
# get hold types
@app.route('/get_hold_types', methods=['GET'])
def get_hold_types():
connection = config.get_db_connection()
try:
cursor = connection.cursor(dictionary=True)
# cursor.execute("SELECT hold_type_id, hold_type FROM hold_types")
# hold_types = cursor.fetchall()
cursor.callproc("GetAllHoldTypes")
for hold in cursor.stored_results():
hold_types = hold.fetchall()
return jsonify(hold_types)
except mysql.connector.Error as e:
return ResponseHandler.fetch_failure({str(e)}), 500
# return jsonify({"status": "error", "message": f"Failed to fetch hold types: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
# update invoice by id
@app.route('/edit_invoice/<int:invoice_id>', methods=['GET', 'POST'])
def edit_invoice(invoice_id):
connection = config.get_db_connection()
if not connection:
return jsonify({"status": "error", "message": "Database connection failed"}), 500
cursor = connection.cursor(dictionary=True)
if request.method == 'POST':
try:
# Fetch updated form data
subcontractor_id = request.form.get('subcontractor_id', '').strip()
subcontractor_id = int(subcontractor_id) if subcontractor_id else None
village_name = request.form.get('village')
# cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,))
# village_result = cursor.fetchone()
cursor.callproc("GetVillageIdByName", (village_name,))
for rs in cursor.stored_results():
village_result = rs.fetchone()
if not village_result:
return jsonify({"status": "error", "message": "Invalid Village Name"}), 400
village_id = village_result['Village_Id']
pmc_no = request.form.get('pmc_no')
work_type = request.form.get('work_type')
invoice_details = request.form.get('invoice_details')
invoice_date = request.form.get('invoice_date')
invoice_no = request.form.get('invoice_no')
# Convert numeric fields properly
numeric_fields = {
"basic_amount": request.form.get('basic_amount'),
"debit_amount": request.form.get('debit_amount'),
"after_debit_amount": request.form.get('after_debit_amount'),
"amount": request.form.get('amount'),
"gst_amount": request.form.get('gst_amount'),
"tds_amount": request.form.get('tds_amount'),
"sd_amount": request.form.get('sd_amount'),
"on_commission": request.form.get('on_commission'),
"hydro_testing": request.form.get('hydro_testing'),
"gst_sd_amount": request.form.get('gst_sd_amount'),
"final_amount": request.form.get('final_amount'),
}
numeric_fields = {k: float(v) if v else 0 for k, v in numeric_fields.items()}
# # Update invoice
# update_invoice_query = '''
# UPDATE invoice
# SET PMC_No=%s, Village_Id=%s, Work_Type=%s, Invoice_Details=%s, Invoice_Date=%s,
# Invoice_No=%s, Basic_Amount=%s, Debit_Amount=%s, After_Debit_Amount=%s,
# Amount=%s, GST_Amount=%s, TDS_Amount=%s, SD_Amount=%s, On_Commission=%s,
# Hydro_Testing=%s, GST_SD_Amount=%s, Final_Amount=%s
# WHERE Invoice_Id=%s
# '''
# invoice_values = (
# pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no,
# *numeric_fields.values(), invoice_id
# )
# cursor.execute(update_invoice_query, invoice_values)
# connection.commit()
cursor.callproc('UpdateInvoice', [
pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no,
*numeric_fields.values(), invoice_id
])
connection.commit()
# Handle holds
hold_types = request.form.getlist('hold_type[]')
hold_amounts = request.form.getlist('hold_amount[]')
for hold_type, hold_amount in zip(hold_types, hold_amounts):
if not hold_type:
continue # skip empty hold types
# Get or insert hold type
# cursor.execute("SELECT hold_type_id FROM hold_types WHERE hold_type = %s", (hold_type,))
# hold_type_result = cursor.fetchone()
cursor.callproc('GetHoldTypeIdByName', [hold_type])
for result in cursor.stored_results():
hold_type_result = result.fetchone()
# if not hold_type_result:
# cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,))
# connection.commit()
# hold_type_id = cursor.lastrowid
# else:
# hold_type_id = hold_type_result['hold_type_id']
if not hold_type_result:
# Call stored procedure to insert and return new ID
cursor.callproc('InsertHoldType', [hold_type, 0])
for result in cursor.stored_results():
pass # advance past any results
cursor.execute("SELECT @_InsertHoldType_1")
hold_type_id = cursor.fetchone()[0]
print("if not hold type result anish:", hold_type_id)
else:
hold_type_id = hold_type_result['hold_type_id']
print("if hold type result anish:", hold_type_id)
hold_amount = float(hold_amount) if hold_amount else 0
# Check if join exists
# cursor.execute("""
# SELECT join_id FROM invoice_subcontractor_hold_join
# WHERE Invoice_Id = %s AND Contractor_Id = %s AND hold_type_id = %s
# """, (invoice_id, subcontractor_id, hold_type_id))
# join_result = cursor.fetchone()
cursor.callproc('GetHoldJoinId', [invoice_id, subcontractor_id, hold_type_id])
for result in cursor.stored_results():
join_result = result.fetchone()
if join_result:
# cursor.execute("""
# UPDATE invoice_subcontractor_hold_join
# SET hold_amount = %s
# WHERE join_id = %s
# """, (hold_amount, join_result['join_id']))
cursor.callproc('UpdateHoldAmountByJoinId', [hold_amount, join_result['join_id']])
connection.commit()
else:
# cursor.execute("""
# INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount)
# VALUES (%s, %s, %s, %s)
# """, (subcontractor_id, invoice_id, hold_type_id, hold_amount))
cursor.callproc('InsertInvoiceSubcontractorHold', [
subcontractor_id, invoice_id, hold_type_id, hold_amount
])
connection.commit()
connection.commit()
return jsonify({"status": "success", "message": "Invoice updated successfully"}), 200
except mysql.connector.Error as e:
connection.rollback()
return jsonify({"status": "error", "message": f"Failed to update invoice: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
# ------------------ GET Request ------------------
try:
# Fetch invoice data
# cursor.execute(
# """SELECT i.*, s.Contractor_Name, v.Village_Name
# FROM invoice i
# LEFT JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no AND i.Village_Id = a.Village_Id
# LEFT JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id
# LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# WHERE i.Invoice_Id = %s""", (invoice_id,)
# )
# invoice = cursor.fetchone()
cursor.callproc('GetInvoiceDetailsById', [invoice_id])
for result in cursor.stored_results():
invoice = result.fetchone()
if not invoice:
return jsonify({"status": "error", "message": "Invoice not found"}), 404
# Important! Clear unread result issue
while cursor.nextset():
pass
# Fetch hold amounts
# cursor.execute(
# """SELECT h.hold_type, ihj.hold_amount
# FROM invoice_subcontractor_hold_join ihj
# JOIN hold_types h ON ihj.hold_type_id = h.hold_type_id
# WHERE ihj.Invoice_Id = %s""", (invoice_id,)
# )
# hold_amounts = cursor.fetchall()
# invoice["hold_amounts"] = hold_amounts
cursor.callproc('GetHoldAmountsByInvoiceId', [invoice_id])
for result in cursor.stored_results():
hold_amounts = result.fetchall()
invoice["hold_amounts"] = hold_amounts
except mysql.connector.Error as e:
return jsonify({"status": "error", "message": f"Database error: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
return render_template('edit_invoice.html', invoice=invoice)
# delete invoice by id
@app.route('/delete_invoice/<int:invoice_id>', methods=['GET'])
def delete_invoice(invoice_id):
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("invoice"), 500)
try:
cursor = connection.cursor()
# cursor.execute("DELETE FROM invoice WHERE Invoice_Id = %s", (invoice_id,))
cursor.callproc("DeleteInvoice", (invoice_id,))
connection.commit()
# Check if the invoice was actually deleted
if cursor.rowcount == 0:
return json_response(ResponseHandler.fetch_failure("invoice"), 404)
return redirect(url_for('add_invoice'))
except mysql.connector.Error as e:
print("Error deleting invoice:", e)
return json_response(ResponseHandler.delete_failure("invoice"), 500)
finally:
cursor.close()
connection.close()
# ---------- end Invoice controller ------------------
# ----------------------------- Payment controller ------------------------------------------
# this is Payment Page to add data
# @app.route('/add_payment', methods=['GET', 'POST'])
# def add_payment():
# connection = config.get_db_connection()
# payments = [] # List to hold payment history
#
# if not connection:
# return json_response(ResponseHandler.fetch_failure("payment"), 500)
#
# try:
# cursor = connection.cursor()
#
# # Retrieve payment history
# # cursor.execute(
# # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment"
# # )
# # payments = cursor.fetchall()
# cursor.callproc("GetAllPayments")
# for result in cursor.stored_results():
# payments = result.fetchall()
#
# except mysql.connector.Error as e:
# print(f"Error fetching payment history: {e}")
# return json_response(ResponseHandler.fetch_failure("payment"), 500)
# finally:
# cursor.close()
#
# if request.method == 'POST':
# pmc_no = request.form['PMC_No']
# invoice_no = request.form['invoice_No']
# amount = request.form['Payment_Amount']
# tds_amount = request.form['TDS_Payment_Amount']
# total_amount = request.form['total_amount']
# utr = request.form['utr']
#
# try:
# cursor = connection.cursor()
# cursor.callproc('SavePayment', (
# pmc_no, invoice_no, amount, tds_amount, total_amount, utr
# ))
# connection.commit()
# return redirect(url_for('add_payment')) # Redirect to add_payment page to reload the form
# except mysql.connector.Error as e:
# print(f"Error inserting payment: {e}")
# return json_response(ResponseHandler.add_failure("payment"), 500)
# finally:
# cursor.close()
# connection.close()
#
# return render_template('add_payment.html', payments=payments)
@app.route('/add_payment', methods=['GET', 'POST'])
def add_payment():
connection = config.get_db_connection()
payments = []
if connection:
cursor = connection.cursor()
try:
# cursor.execute(
# "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment"
# )
# payments = cursor.fetchall()
cursor.callproc('GetAllPayments')
for result in cursor.stored_results():
payments = result.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching payment history: {e}")
return "Failed to fetch payment history", 500
finally:
cursor.close()
if request.method == 'POST':
pmc_no = request.form['PMC_No']
invoice_no = request.form['invoice_No']
amount = request.form['Payment_Amount']
tds_amount = request.form['TDS_Payment_Amount']
total_amount = request.form['total_amount']
utr = request.form['utr']
try:
cursor = connection.cursor()
# cursor.execute('''INSERT INTO payment (PMC_No, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)
# VALUES (%s, %s, %s, %s, %s, %s)''',
# (pmc_no, invoice_no, amount, tds_amount, total_amount, utr))
# connection.commit()
cursor.callproc('InsertPayments', [
pmc_no, invoice_no, amount, tds_amount, total_amount, utr
])
connection.commit()
return redirect(url_for('add_payment'))
except mysql.connector.Error as e:
print(f"Error inserting payment: {e}")
return "Failed to add payment", 500
finally:
cursor.close()
connection.close()
return render_template('add_payment.html', payments=payments)
@app.route('/get_pmc_nos_by_subcontractor/<subcontractorId>')
def get_pmc_nos_by_subcontractor(subcontractorId):
connection = config.get_db_connection()
cur = connection.cursor()
print(subcontractorId)
# query = """
# SELECT DISTINCT i.PMC_No
# FROM invoice i
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no
# JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id
# WHERE s.Contractor_Id=%s;
# """
# cur.execute(query, (subcontractorId,))
# results = cur.fetchall()
cur.callproc('GetDistinctPMCNoByContractorId', [subcontractorId])
for result in cur.stored_results():
results = result.fetchall()
print(results)
pmc_nos = [row[0] for row in results]
cur.close()
return jsonify({'pmc_nos': pmc_nos})
# Edit Payment Route
@app.route('/edit_payment/<int:payment_id>', methods=['GET', 'POST'])
def edit_payment(payment_id):
connection = config.get_db_connection()
payment_data = {} # To hold the payment data for the given ID
if not connection:
return json_response(ResponseHandler.fetch_failure("payment"), 500)
try:
cursor = connection.cursor()
# Fetch the existing payment data for the given payment_id
# cursor.execute(
# "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment WHERE Payment_Id = %s",
# (payment_id,)
# )
# payment_data = cursor.fetchone()
cursor.callproc("GetPaymentById", (payment_id,))
for result in cursor.stored_results():
payment_data = result.fetchone()
# Handle POST request to update the payment
if request.method == 'POST':
pmc_no = request.form['PMC_No']
invoice_no = request.form['invoice_No']
amount = request.form['Payment_Amount']
tds_amount = request.form['TDS_Payment_Amount']
total_amount = request.form['total_amount']
utr = request.form['utr']
try:
# cursor.execute('''UPDATE payment SET PMC_No=%s, Invoice_No=%s, Payment_Amount=%s, TDS_Payment_Amount=%s,
# Total_Amount=%s, UTR=%s WHERE Payment_Id=%s''',
# (pmc_no, invoice_no, amount, tds_amount, total_amount, utr, payment_id))
cursor.callproc("UpdatePayment",
(payment_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr,))
connection.commit()
return redirect(url_for('add_payment')) # Redirect to add_payment page to view the updated list
except mysql.connector.Error as e:
print(f"Error updating payment: {e}")
return json_response(ResponseHandler.update_failure("payment"), 500)
except mysql.connector.Error as e:
print(f"Error fetching payment data: {e}")
return json_response(ResponseHandler.fetch_failure("payment"), 500)
finally:
cursor.close()
connection.close()
return render_template('edit_payment.html', payment_data=payment_data)
# Delete Payment Route
@app.route('/delete_payment/<int:payment_id>', methods=['GET', 'POST'])
def delete_payment(payment_id):
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("payment"), 500)
try:
cursor = connection.cursor()
# cursor.execute("DELETE FROM payment WHERE Payment_Id = %s", (payment_id,))
cursor.callproc("DeletePayment", (payment_id,))
connection.commit()
# Check if any rows were deleted
if cursor.rowcount == 0:
return json_response(ResponseHandler.fetch_failure("payment"), 404)
return redirect(url_for('add_payment')) # Redirect back to the add_payment page
except mysql.connector.Error as e:
print(f"Error deleting payment: {e}")
return json_response(ResponseHandler.delete_failure("payment"), 500)
finally:
cursor.close()
connection.close()
# --- end Payment controller -----------
# ------------------------- GST Release controller ------------------------------------------
@app.route('/add_gst_release', methods=['GET', 'POST'])
def add_gst_release():
connection = config.get_db_connection()
gst_releases = [] # List to hold GST Release history
invoices = [] # List to hold invoices for the dropdown
if not connection:
return json_response(ResponseHandler.fetch_failure("GST Release"), 500)
try:
cursor = connection.cursor()
# Retrieve GST Release history
# cursor.execute("SELECT GST_Release_Id, PMC_No, Invoice_No, Basic_Amount, Final_Amount FROM gst_release")
# gst_releases = cursor.fetchall()
cursor.callproc("GetAllGSTReleases")
for result in cursor.stored_results():
gst_releases = result.fetchall()
if request.method == 'POST':
pmc_no = request.form['PMC_No']
invoice_no = request.form['invoice_No']
basic_amount = request.form['basic_amount']
final_amount = request.form['final_amount']
cursor.callproc('SaveGSTRelease', (
pmc_no, invoice_no, basic_amount, final_amount
))
connection.commit()
return redirect(url_for('add_gst_release')) # Redirect to add_gst_release page
except mysql.connector.Error as e:
print(f"Error: {e}")
return json_response(ResponseHandler.add_failure("GST Release"), 500)
finally:
cursor.close()
connection.close()
return render_template('add_gst_release.html', invoices=invoices, gst_releases=gst_releases)
# update gst Release by id
@app.route('/edit_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
def edit_gst_release(gst_release_id):
connection = config.get_db_connection()
gst_release_data = {} # To hold the GST release data for the given ID
invoices = [] # List to hold invoices for the dropdown
if not connection:
return json_response(ResponseHandler.fetch_failure("GST Release"), 500)
try:
cursor = connection.cursor()
# Fetch the existing GST release data for the given gst_release_id
# cursor.execute(
# "SELECT GST_Release_Id, PMC_No, Invoice_No, Basic_Amount, Final_Amount FROM gst_release WHERE GST_Release_Id = %s",
# (gst_release_id,)
# )
# gst_release_data = cursor.fetchone()
cursor.callproc("GetGSTReleaseById", (gst_release_id,))
for result in cursor.stored_results():
gst_release_data = result.fetchone()
if request.method == 'POST':
pmc_id = request.form['PMC_No']
invoice_no = request.form['invoice_No']
basic_amount = request.form['basic_amount']
final_amount = request.form['final_amount']
try:
# cursor.execute('''UPDATE gst_release SET PMC_No=%s, Invoice_No=%s, Basic_Amount=%s, Final_Amount=%s
# WHERE GST_Release_Id=%s''',
# (pmc_id, invoice_no, basic_amount, final_amount, gst_release_id))
cursor.callproc("UpdateGSTRelease", (gst_release_id, pmc_id, invoice_no, basic_amount, final_amount))
connection.commit()
return redirect(url_for('add_gst_release')) # Redirect to the page to view the updated list
except mysql.connector.Error as e:
print(f"Error updating GST Release: {e}")
return json_response(ResponseHandler.update_failure("GST Release"), 500)
except mysql.connector.Error as e:
print(f"Error fetching GST Release data: {e}")
return json_response(ResponseHandler.fetch_failure("GST Release"), 500)
finally:
cursor.close()
connection.close()
return render_template('edit_gst_release.html', gst_release_data=gst_release_data, invoices=invoices)
# delete gst release by id
@app.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
def delete_gst_release(gst_release_id):
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("GST Release"), 500)
try:
cursor = connection.cursor()
# cursor.execute("DELETE FROM gst_release WHERE GST_Release_Id = %s", (gst_release_id,))
cursor.callproc("DeleteGSTRelease", (gst_release_id,))
connection.commit()
# Check if any rows were deleted
if cursor.rowcount == 0:
return json_response(ResponseHandler.fetch_failure("GST Release"), 404)
return redirect(url_for('add_gst_release')) # Redirect to the add_gst_release page
except mysql.connector.Error as e:
print(f"Error deleting GST Release: {e}")
return json_response(ResponseHandler.delete_failure("GST Release"), 500)
finally:
cursor.close()
connection.close()
# --- end GST Release controller -----
# ------------------------- Subcontractor controller ------------------------------------------
@app.route('/subcontractor', methods=['GET', 'POST'])
def subcontract():
connection = config.get_db_connection()
subcontractor = []
if not connection:
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
try:
cursor = connection.cursor()
if request.method == 'GET':
try:
# cursor.execute('SELECT * FROM subcontractors;')
# subcontractor = cursor.fetchall() # Fetch the current subcontractor list
# connection.commit()
cursor.callproc('GetAllSubcontractors')
for result in cursor.stored_results():
subcontractor = result.fetchall()
except Error as e:
print(f"Error fetching data: {e}")
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
if request.method == 'POST':
contractor_data = {
'Contractor_Name': request.form['Contractor_Name'],
'Address': request.form['Address'],
'Mobile_No': request.form['Mobile_No'],
'PAN_No': request.form['PAN_No'],
'Email': request.form['Email'],
'Gender': request.form['Gender'],
'GST_Registration_Type': request.form['GST_Registration_Type'],
'GST_No': request.form['GST_No'],
'Contractor_password': request.form['Contractor_password'],
}
try:
cursor.callproc('SaveContractor', (
contractor_data['Contractor_Name'],
contractor_data['Address'],
contractor_data['Mobile_No'],
contractor_data['PAN_No'],
contractor_data['Email'],
contractor_data['Gender'],
contractor_data['GST_Registration_Type'],
contractor_data['GST_No'],
contractor_data['Contractor_password']
))
connection.commit()
# Re-fetch subcontractors after inserting the new one
# cursor.execute('SELECT * FROM subcontractors')
# subcontractor = cursor.fetchall()
cursor.callproc('GetAllSubcontractors')
for result in cursor.stored_results():
subcontractor = result.fetchall()
except Error as e:
print(f"Error inserting data: {e}")
return json_response(ResponseHandler.add_failure("Subcontractor"), 500)
except Error as e:
print(f"Error handling subcontractor data: {e}")
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
finally:
cursor.close()
connection.close()
return render_template('add_subcontractor.html', subcontractor=subcontractor)
# update subcontractor by id
@app.route('/edit_subcontractor/<int:id>', methods=['GET', 'POST'])
def edit_subcontractor(id):
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
try:
cursor = connection.cursor()
subcontractor = None
# Fetch existing subcontractor data by ID
# cursor.execute('SELECT * FROM subcontractors WHERE Contractor_Id = %s', (id,))
# subcontractor = cursor.fetchone()
cursor.callproc("GetSubcontractorById", (id,))
for contractors in cursor.stored_results():
subcontractor = contractors.fetchone()
if not subcontractor:
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 404)
if request.method == 'POST':
updated_data = {
'Contractor_Name': request.form['Contractor_Name'],
'Address': request.form['Address'],
'Mobile_No': request.form['Mobile_No'],
'PAN_No': request.form['PAN_No'],
'Email': request.form['Email'],
'Gender': request.form['Gender'],
'GST_Registration_Type': request.form['GST_Registration_Type'],
'GST_No': request.form['GST_No'],
'Contractor_password': request.form['Contractor_password'],
'id': id
}
try:
# cursor.execute("""UPDATE subcontractors SET
# Contractor_Name=%(Contractor_Name)s,
# Address=%(Address)s,
# Mobile_No=%(Mobile_No)s,
# PAN_No=%(PAN_No)s,
# Email=%(Email)s,
# Gender=%(Gender)s,
# GST_Registration_Type=%(GST_Registration_Type)s,
# GST_No=%(GST_No)s,
# Contractor_password=%(Contractor_password)s
# WHERE Contractor_Id=%(id)s""", updated_data)
cursor.callproc("UpdateSubcontractor", (
id,
updated_data['Contractor_Name'],
updated_data['Address'],
updated_data['Mobile_No'],
updated_data['PAN_No'],
updated_data['Email'],
updated_data['Gender'],
updated_data['GST_Registration_Type'],
updated_data['GST_No'],
updated_data['Contractor_password']
))
connection.commit()
return redirect(url_for('subcontract'))
except Error as e:
print(f"Error updating subcontractor: {e}")
return json_response(ResponseHandler.update_failure("Subcontractor"), 500)
except Error as e:
print(f"Error fetching subcontractor data: {e}")
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
finally:
cursor.close()
connection.close()
return render_template('edit_subcontractor.html', subcontractor=subcontractor)
# delete Sub-Contractor methods by id ..
# @app.route('/deleteSubContractor/<int:id>', methods=['GET', 'POST'])
# def deleteSubContractor(id):
# connection = config.get_db_connection()
# if not connection:
# return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
# try:
# cursor = connection.cursor()
# # cursor.execute("DELETE FROM subcontractors WHERE Contractor_Id = %s", (id,))
# cursor.callproc("DeleteSubcontractor", (id,))
# connection.commit()
# # Check if any row was deleted (subcontractor found)
# if cursor.rowcount == 0:
# return json_response(ResponseHandler.fetch_failure("Subcontractor"), 404)
# except Error as e:
# print(f"Error deleting subcontractor: {e}")
# return json_response(ResponseHandler.delete_failure("Subcontractor"), 500)
# finally:
# cursor.close()
# connection.close()
# return redirect(url_for('subcontract'))
@app.route('/deleteSubContractor/<int:id>', methods=['GET', 'POST'])
def deleteSubContractor(id):
connection = config.get_db_connection()
if not connection:
return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500)
try:
cursor = connection.cursor()
# Optional: check if subcontractor exists before attempting delete
cursor.execute("SELECT 1 FROM subcontractors WHERE Contractor_Id = %s", (id,))
if cursor.fetchone() is None:
return json_response(ResponseHandler.fetch_failure("Subcontractor not found"), 404)
# Call stored procedure to delete subcontractor and related records
cursor.callproc("DeleteSubcontractor", (id,))
connection.commit()
# Retrieve result from procedure (SELECT ROW_COUNT())
affected_rows = 0
for result in cursor.stored_results():
row = result.fetchone()
affected_rows = row[0] if row else 0
if affected_rows == 0:
return json_response(ResponseHandler.fetch_failure("Subcontractor not deleted"), 404)
except Error as e:
print(f"Error deleting subcontractor: {e}")
return json_response(ResponseHandler.delete_failure("Subcontractor"), 500)
finally:
cursor.close()
connection.close()
return redirect(url_for('subcontract')) # redirect to subcontractor list page
# ------------------------------- Show Report Subcontractor ---------------------
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
# Upload Excel file html page
@app.route('/upload_excel_file', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
file = request.files['file']
if file and file.filename.endswith('.xlsx'):
filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
file.save(filepath)
return redirect(url_for('show_table', filename=file.filename))
return render_template('uploadExcelFile.html')
# Show excel data in tables6
# @app.route('/show_table/<filename>')
# def show_table(filename):
# global data
# data = []
#
# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
# wb = openpyxl.load_workbook(filepath, data_only=True)
# sheet = wb.active
#
# # Extract key file information from the first 4 rows
# file_info = {
# "Subcontractor": sheet.cell(row=1, column=2).value,
# "State": sheet.cell(row=2, column=2).value,
# "District": sheet.cell(row=3, column=2).value,
# "Block": sheet.cell(row=4, column=2).value,
# }
#
# errors = []
# subcontractor_data = None
# state_data = None
# district_data = None
# block_data = None
#
# # Database connection
# connection = config.get_db_connection()
# if connection:
# try:
# cursor = connection.cursor(dictionary=True)
#
# # Validate State
# # cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],))
# # state_data = cursor.fetchone()
# cursor.callproc('GetStateByName', [file_info['State']])
# for result in cursor.stored_results():
# state_data = result.fetchone()
#
# if not state_data:
# errors.append(f"State '{file_info['State']}' is not valid. Please add it.")
#
# # Validate District
# if state_data:
# # cursor.execute(
# # "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s",
# # (file_info['District'], state_data['State_ID'])
# # )
# # district_data = cursor.fetchone()
# cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']])
# for result in cursor.stored_results():
# district_data = result.fetchone()
#
# if not district_data:
# errors.append(
# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.")
#
# # Validate Block
# if district_data:
# # cursor.execute(
# # "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s",
# # (file_info['Block'], district_data['District_ID'])
# # )
# # block_data = cursor.fetchone()
# cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']])
# for result in cursor.stored_results():
# block_data = result.fetchone()
#
# if not block_data:
# errors.append(
# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.")
#
# # old code
# # # Validate Subcontractor
# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s",
# # (file_info['Subcontractor'],))
# # subcontractor_data = cursor.fetchone()
# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
# for result in cursor.stored_results():
# subcontractor_data = result.fetchone()
#
# if not subcontractor_data:
# # cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)",
# # (file_info['Subcontractor'],))
# # connection.commit()
# cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']])
# connection.commit()
#
# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s",
# # (file_info['Subcontractor'],))
# # subcontractor_data = cursor.fetchone()
# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
# for result in cursor.stored_results():
# subcontractor_data = result.fetchone()
#
# # new code
# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, ''))
# #
# # for con in cursor.stored_results():
# # subcontractor_data = con.fetchone()
# # print("subcon:",subcontractor_data)
# #
# # print("subcontractor_data",subcontractor_data)
#
# # Get hold types data from database (for faster lookup)
# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types")
# # hold_types_data = cursor.fetchall()
#
# cursor.callproc("GetAllHoldTypes")
# for ht in cursor.stored_results():
# hold_types_data = ht.fetchall()
#
# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if
# row['hold_type']}
#
# cursor.close()
# except mysql.connector.Error as e:
# print(f"Database error: {e}")
# return "Database operation failed", 500
# finally:
# connection.close()
#
# # Extract dynamic variable names from row 5 and detect "hold" columns
# variables = {}
# hold_columns = []
# hold_counter = 0
#
# for j in range(1, sheet.max_column + 1):
# col_value = sheet.cell(row=5, column=j).value
# if col_value:
# variables[col_value] = j # Store column name with its position
#
# # Check if the column header contains the word 'hold'
# if 'hold' in str(col_value).lower():
# hold_counter += 1
# # Lookup hold type id from database
# hold_type_key = str(col_value).lower().strip()
# hold_type_id = hold_types_lookup.get(hold_type_key, None)
# hold_columns.append({
# 'column_name': col_value,
# 'column_number': j,
# 'hold_type_id': hold_type_id
# })
#
# # Extract data dynamically based on row numbers
# for i in range(6, sheet.max_row + 1):
# row_data = {}
# if sheet.cell(row=i, column=1).value:
# row_data["Row Number"] = i # Store row number
# for var_name, col_num in variables.items():
# row_data[var_name] = sheet.cell(row=i, column=col_num).value
# # Check if at least 4 non-empty cells exist in the row
# if sum(1 for value in row_data.values() if value) >= 4:
# data.append(row_data)
#
# # For debugging or console output, you can print the hold columns info
# for hold in hold_columns:
# if hold['hold_type_id']:
# print(
# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
# else:
# errors.append(
# f"Hold Type not added ! Column name '{hold['column_name']}'.")
# print(
# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
#
# return render_template(
# 'show_excel_file.html',
# file_info=file_info,
# variables=variables,
# data=data,
# subcontractor_data=subcontractor_data,
# state_data=state_data,
# district_data=district_data,
# block_data=block_data,
# errors=errors,
# hold_columns=hold_columns,
# hold_counter=hold_counter
# )
@app.route('/show_table/<filename>')
def show_table(filename):
global data
data = []
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
wb = openpyxl.load_workbook(filepath, data_only=True)
sheet = wb.active
file_info = {
"Subcontractor": sheet.cell(row=1, column=2).value,
"State": sheet.cell(row=2, column=2).value,
"District": sheet.cell(row=3, column=2).value,
"Block": sheet.cell(row=4, column=2).value,
}
errors = []
subcontractor_data = None
state_data = None
district_data = None
block_data = None
connection = config.get_db_connection()
if connection:
try:
cursor = connection.cursor(dictionary=True)
print(f"Calling GetStateByName with: {file_info['State']}")
cursor.callproc('GetStateByName', [file_info['State']])
for result in cursor.stored_results():
state_data = result.fetchone()
if not state_data:
errors.append(f"State '{file_info['State']}' is not valid. Please add it.")
if state_data:
print(f"Calling GetDistrictByNameAndStates with: {file_info['District']}, {state_data['State_ID']}")
cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']])
for result in cursor.stored_results():
district_data = result.fetchone()
if not district_data:
errors.append(f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.")
if district_data:
print(f"Calling GetBlockByNameAndDistricts with: {file_info['Block']}, {district_data['District_ID']}")
cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']])
for result in cursor.stored_results():
block_data = result.fetchone()
if not block_data:
errors.append(f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.")
print(f"Calling GetSubcontractorByName with: {file_info['Subcontractor']}")
cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
for result in cursor.stored_results():
subcontractor_data = result.fetchone()
if not subcontractor_data:
print(f"Inserting subcontractor: {file_info['Subcontractor']}")
cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']])
connection.commit()
print(f"Calling GetSubcontractorByName again with: {file_info['Subcontractor']}")
cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
for result in cursor.stored_results():
subcontractor_data = result.fetchone()
print("Calling GetAllHoldTypes")
cursor.callproc("GetAllHoldTypes")
hold_types_data = []
for ht in cursor.stored_results():
hold_types_data = ht.fetchall()
hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if row['hold_type']}
cursor.close()
except mysql.connector.Error as e:
print(f"Database error: {e}")
return f"Database operation failed: {e}", 500
finally:
connection.close()
variables = {}
hold_columns = []
hold_counter = 0
for j in range(1, sheet.max_column + 1):
col_value = sheet.cell(row=5, column=j).value
if col_value:
variables[col_value] = j
if 'hold' in str(col_value).lower():
hold_counter += 1
hold_type_key = str(col_value).lower().strip()
hold_type_id = hold_types_lookup.get(hold_type_key, None)
hold_columns.append({
'column_name': col_value,
'column_number': j,
'hold_type_id': hold_type_id
})
for i in range(6, sheet.max_row + 1):
row_data = {}
if sheet.cell(row=i, column=1).value:
row_data["Row Number"] = i
for var_name, col_num in variables.items():
row_data[var_name] = sheet.cell(row=i, column=col_num).value
if sum(1 for value in row_data.values() if value) >= 4:
data.append(row_data)
for hold in hold_columns:
if hold['hold_type_id']:
print(f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
else:
errors.append(f"Hold Type not added ! Column name '{hold['column_name']}'.")
print(f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
return render_template(
'show_excel_file.html',
file_info=file_info,
variables=variables,
data=data,
subcontractor_data=subcontractor_data,
state_data=state_data,
district_data=district_data,
block_data=block_data,
errors=errors,
hold_columns=hold_columns,
hold_counter=hold_counter
)
# Show excel data in tables6
# @app.route('/show_table/<filename>')
# def show_table(filename):
# global data
# data = []
#
# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
# wb = openpyxl.load_workbook(filepath, data_only=True)
# sheet = wb.active
#
# # Extract key file information from the first 4 rows
# file_info = {
# "Subcontractor": sheet.cell(row=1, column=2).value,
# "State": sheet.cell(row=2, column=2).value,
# "District": sheet.cell(row=3, column=2).value,
# "Block": sheet.cell(row=4, column=2).value,
# }
#
# errors = []
# subcontractor_data = None
# state_data = None
# district_data = None
# block_data = None
#
# # Database connection
# connection = config.get_db_connection()
# if connection:
# try:
# cursor = connection.cursor(dictionary=True)
#
# # Validate State
# cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],))
# state_data = cursor.fetchone()
# if not state_data:
# errors.append(f"State '{file_info['State']}' is not valid. Please add it.")
#
# # Validate District
# if state_data:
# cursor.execute(
# "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s",
# (file_info['District'], state_data['State_ID'])
# )
# district_data = cursor.fetchone()
# if not district_data:
# errors.append(
# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.")
#
# # Validate Block
# if district_data:
# cursor.execute(
# "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s",
# (file_info['Block'], district_data['District_ID'])
# )
# block_data = cursor.fetchone()
# if not block_data:
# errors.append(
# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.")
#
#
# # old code
# # # Validate Subcontractor
# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s",
# (file_info['Subcontractor'],))
# subcontractor_data = cursor.fetchone()
#
# if not subcontractor_data:
# cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)",
# (file_info['Subcontractor'],))
# connection.commit()
# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s",
# (file_info['Subcontractor'],))
# subcontractor_data = cursor.fetchone()
#
# # new code
# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, ''))
# #
# # for con in cursor.stored_results():
# # subcontractor_data = con.fetchone()
# # print("subcon:",subcontractor_data)
# #
# # print("subcontractor_data",subcontractor_data)
#
# # Get hold types data from database (for faster lookup)
# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types")
# # hold_types_data = cursor.fetchall()
#
# cursor.callproc("GetAllHoldTypes")
# for ht in cursor.stored_results():
# hold_types_data = ht.fetchall()
#
#
# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if row['hold_type']}
#
#
# cursor.close()
# except mysql.connector.Error as e:
# print(f"Database error: {e}")
#
# # return "Database operation failed", 500
# return f"{e}",500
# finally:
# connection.close()
#
# # Extract dynamic variable names from row 5 and detect "hold" columns
# variables = {}
# hold_columns = []
# hold_counter = 0
#
# for j in range(1, sheet.max_column + 1):
# col_value = sheet.cell(row=5, column=j).value
# if col_value:
# variables[col_value] = j # Store column name with its position
#
# # Check if the column header contains the word 'hold'
# if 'hold' in str(col_value).lower():
# hold_counter += 1
# # Lookup hold type id from database
# hold_type_key = str(col_value).lower().strip()
# hold_type_id = hold_types_lookup.get(hold_type_key, None)
# hold_columns.append({
# 'column_name': col_value,
# 'column_number': j,
# 'hold_type_id': hold_type_id
# })
#
# # Extract data dynamically based on row numbers
# for i in range(6, sheet.max_row + 1):
# row_data = {}
# if sheet.cell(row=i, column=1).value:
# row_data["Row Number"] = i # Store row number
# for var_name, col_num in variables.items():
# row_data[var_name] = sheet.cell(row=i, column=col_num).value
# # Check if at least 4 non-empty cells exist in the row
# if sum(1 for value in row_data.values() if value) >= 4:
# data.append(row_data)
#
# # For debugging or console output, you can print the hold columns info
# for hold in hold_columns:
# if hold['hold_type_id']:
# print(
# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
# else:
# errors.append(
# f"Hold Type not added ! Column name '{hold['column_name']}'.")
# print(
# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
#
# return render_template(
# 'show_excel_file.html',
# file_info=file_info,
# variables=variables,
# data=data,
# subcontractor_data=subcontractor_data,
# state_data=state_data,
# district_data=district_data,
# block_data=block_data,
# errors=errors,
# hold_columns=hold_columns,
# hold_counter=hold_counter
# )
# save Excel data
@app.route('/save_data', methods=['POST'])
def save_data():
# Extract form data
subcontractor_id = request.form.get("subcontractor_data")
state_id = request.form.get("state_data")
district_id = request.form.get("district_data")
block_id = request.form.get("block_data")
variables = request.form.getlist('variables[]')
hold_columns = request.form.get("hold_columns")
hold_counter = request.form.get("hold_counter")
# print("Info: ", subcontractor_id, state_id, district_id, block_id)
if not data:
return jsonify({"error": "No data provided to save"}), 400
if data:
# print("Total number of entries in data:", len(data))
connection = config.get_db_connection()
cursor = connection.cursor()
try:
for entry in data:
save_data = {
"PMC_No": entry.get("PMC_No"),
"Invoice_Details": entry.get("Invoice_Details", ''),
"Work_Type": 'none',
"Invoice_Date": entry.get("Invoice_Date").strftime('%Y-%m-%d') if entry.get(
"Invoice_Date") else None,
"Invoice_No": entry.get("Invoice_No", ''),
"Basic_Amount": entry.get("Basic_Amount", 0.00),
"Debit_Amount": entry.get("Debit_Amount", 0.00),
"After_Debit_Amount": entry.get("After_Debit_Amount", 0.00),
"Amount": entry.get("Amount", 0.00),
"GST_Amount": entry.get("GST_Amount", 0.00),
"TDS_Amount": entry.get("TDS_Amount", 0.00),
"SD_Amount": entry.get("SD_Amount", 0.00),
"On_Commission": entry.get("On_Commission", 0.00),
"Hydro_Testing": entry.get("Hydro_Testing", 0.00),
"Hold_Amount": 0,
"GST_SD_Amount": entry.get("GST_SD_Amount", 0.00),
"Final_Amount": entry.get("Final_Amount", 0.00),
"Payment_Amount": entry.get("Payment_Amount", 0.00),
"Total_Amount": entry.get("Total_Amount", 0.00),
"TDS_Payment_Amount": entry.get("TDS_Payment_Amount", 0.00),
"UTR": entry.get("UTR", ''),
}
village_name, work_type = None, None
village_id = 0
PMC_No = save_data.get('PMC_No')
Invoice_Details = save_data.get('Invoice_Details')
Invoice_Date = save_data.get('Invoice_Date')
Invoice_No = save_data.get('Invoice_No')
Basic_Amount = save_data.get('Basic_Amount')
Debit_Amount = save_data.get('Debit_Amount')
After_Debit_Amount = save_data.get('After_Debit_Amount')
Amount = save_data.get('Amount')
GST_Amount = save_data.get('GST_Amount')
TDS_Amount = save_data.get('TDS_Amount')
SD_Amount = save_data.get('SD_Amount')
On_Commission = save_data.get('On_Commission')
Hydro_Testing = save_data.get('Hydro_Testing')
GST_SD_Amount = save_data.get('GST_SD_Amount')
Final_Amount = save_data.get('Final_Amount')
Payment_Amount = save_data.get('Payment_Amount')
Total_Amount = save_data.get('Total_Amount')
TDS_Payment_Amount = save_data.get('TDS_Payment_Amount')
UTR = save_data.get('UTR')
if Invoice_Details:
words = Invoice_Details.lower().split()
if 'village' in words:
village_pos = words.index('village')
village_name = " ".join(words[:village_pos])
if 'work' in words:
work_pos = words.index('work')
if village_name:
work_type = " ".join(words[village_pos + 1:work_pos + 1])
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)
if block_id and village_name:
village_id = None
# cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name))
# result = cursor.fetchone()
cursor.callproc("GetVillageId", (block_id, village_name))
for result in cursor.stored_results():
result = result.fetchone()
village_id = result[0] if result else None
if not village_id:
# cursor.execute("INSERT INTO villages (Village_Name, Block_Id) VALUES (%s, %s)", (village_name, block_id))
cursor.callproc("SaveVillage", (village_name, block_id))
# cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name))
# result = cursor.fetchone()
cursor.callproc("GetVillageId", (block_id, village_name))
for result in cursor.stored_results():
result = result.fetchone()
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)
#
# cursor.execute("SET @p_invoice_id = 0")
# cursor.callproc("SaveInvoice", (
# PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No,
# Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount,
# SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount,
# subcontractor_id, "@p_invoice_id"
# ))
# cursor.execute("SELECT @p_invoice_id")
# invoice_id = cursor.fetchone()[0]
args = (
PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No,
Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount,
SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount,
subcontractor_id, 0
)
# for result in cursor.stored_results():
# invoice_id = result.fetchone()['invoice_id']
results = cursor.callproc('SaveInvoice', args)
# cursor.callproc("SaveInvoice",args)
# for re in cursor.stored_results():
invoice_id = results[-1]
print("invoice id from the excel ", invoice_id)
if isinstance(hold_columns, str):
hold_columns = ast.literal_eval(hold_columns)
# Check if hold_columns is actually a list of dictionaries
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}")
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}")
# Insert into the invoice_subcontractor_hold_join table
hold_join_data = {
"Contractor_Id": subcontractor_id,
"Invoice_Id": invoice_id,
"hold_type_id": hold_type_id,
"hold_amount": hold_amount
}
# insert_hold_query = """INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount)
# VALUES (%(Contractor_Id)s, %(Invoice_Id)s, %(hold_type_id)s, %(hold_amount)s);
# """
# cursor.execute(insert_hold_query, hold_join_data)
# print(f"Inserted hold join data: {hold_join_data}")
cursor.callproc('InsertHoldJoinData', [
hold_join_data['Contractor_Id'], hold_join_data['Invoice_Id'],
hold_join_data['hold_type_id'], hold_join_data['hold_amount']
])
connection.commit()
print(f"Inserted hold join data: {hold_join_data}")
else:
print(f"Invalid hold entry: {hold}")
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']):
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.execute(
"""INSERT INTO credit_note (PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Contractor_Id, invoice_no) VALUES (%s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,%s)""",
( PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, subcontractor_id, Invoice_No))
#-----------------------------------------------Hold Amount----------------------------------------------------------------------
elif Invoice_Details and any(keyword in Invoice_Details.lower() for keyword in
['excess hold', 'ht', 'hold release amount']):
cursor.execute("""
INSERT INTO hold_release (
PMC_No, Invoice_No, Invoice_Details, Basic_Amount, Total_Amount, UTR,Contractor_Id
) VALUES (%s, %s, %s, %s, %s, %s,%s)
""", (
PMC_No, Invoice_No, Invoice_Details, Basic_Amount, Final_Amount, UTR, subcontractor_id))
print("Hold amount release from excel file:", 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)
cursor.callproc("SaveGSTRelease", (PMC_No, Invoice_No, Basic_Amount, Final_Amount))
# cursor.execute(
# """INSERT INTO gst_release (PMC_No, Invoice_No, Basic_Amount, Final_Amount) VALUES (%s,%s, %s, %s)""",
# (PMC_No, Invoice_No, Basic_Amount, Final_Amount))
# insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)"""
# cursor.execute(insert_payment,
# (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR))
if PMC_No and Total_Amount and UTR:
print("Payment :", PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)
# insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)"""
# cursor.execute(insert_payment,
# (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR))
cursor.callproc("SavePayment",
(PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR))
connection.commit()
return jsonify({"success": "Data saved successfully!"}), 200
# return render_template('uploadExcelFile.html')
except Exception as e:
connection.rollback()
return jsonify({"error": f"An unexpected error occurred: {e}"}), 500
finally:
cursor.close()
connection.close()
return render_template('index.html')
# ---------------------- Report --------------------------------
# call repor page
@app.route('/report')
def report_page():
return render_template('report.html')
# Search list multiples input and search reports
@app.route('/search_contractor', methods=['POST'])
def search_contractor():
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True)
subcontractor_name = request.form.get('subcontractor_name')
pmc_no = request.form.get('pmc_no')
state = request.form.get('state')
district = request.form.get('district')
block = request.form.get('block')
village = request.form.get('village')
year_from = request.form.get('year_from')
year_to = request.form.get('year_to')
conditions = []
params = []
if subcontractor_name:
conditions.append("LOWER(s.Contractor_Name) LIKE LOWER(%s)")
params.append(f"%{subcontractor_name}%")
if pmc_no:
conditions.append("i.PMC_No = %s")
params.append(pmc_no)
if state:
conditions.append("LOWER(st.State_Name) LIKE LOWER(%s)")
params.append(f"%{state}%")
if district:
conditions.append("LOWER(d.District_Name) LIKE LOWER(%s)")
params.append(f"%{district}%")
if block:
conditions.append("LOWER(b.Block_Name) LIKE LOWER(%s)")
params.append(f"%{block}%")
if village:
conditions.append("LOWER(v.Village_Name) LIKE LOWER(%s)")
params.append(f"%{village}%")
if year_from and year_to:
conditions.append("i.Invoice_Date BETWEEN %s AND %s")
params.append(year_from)
params.append(year_to)
if not conditions:
return jsonify({"error": "At least one field is required for search."}), 400
# query = f"""
# SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, i.PMC_No, st.State_Name,
# d.District_Name, b.Block_Name, v.Village_Name
# FROM subcontractors s
# INNER JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# INNER JOIN villages v ON asg.Village_Id = v.Village_Id
# INNER JOIN invoice i ON i.Village_Id = asg.Village_Id AND i.PMC_No = asg.PMC_No
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE {' AND '.join(conditions)}
# ORDER BY s.Contractor_Name ASC, i.PMC_No ASC
# """
# cursor.execute(query, tuple(params))
# data = cursor.fetchall()
cursor.callproc("search_contractor_info", [
subcontractor_name or None,
pmc_no or None,
state or None,
district or None,
block or None,
village or None,
year_from or None,
year_to or None
])
for result in cursor.stored_results():
data = result.fetchall()
return jsonify(data)
# @app.route('/contractor_report/<int:contractor_id>')
# def contractor_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True, buffered=True)
#
# try:
# # Fetch contractor details
# cursor.execute("""
# SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# s.Mobile_No, s.GST_Registration_Type, s.GST_No,s.PAN_No,s.Email,s.Address
# FROM subcontractors s
# LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE s.Contractor_Id = %s
# """, (contractor_id,))
# contInfo = cursor.fetchone()
#
# # cursor.callproc('GetContractorInfoById', [contractor_id])
# # contInfo = next(cursor.stored_results()).fetchone()
#
# # Fetch distinct hold types present in invoices for the contractor
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_types = cursor.fetchall()
# # cursor.callproc('GetHoldTypesByContId', [contractor_id])
# # hold_types = next(cursor.stored_results()).fetchall()
#
# # Fetch all invoices for the contractor, with optional hold information
# query = """
# SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# i.After_Debit_Amount, i.Amount, i.GST_Amount, i.TDS_Amount, i.SD_Amount,
# i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
# i.Final_Amount, h.hold_amount, ht.hold_type
# FROM invoice i
# LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# LEFT JOIN assign_subcontractors asg ON v.Village_Id = asg.Village_Id
# LEFT JOIN subcontractors s ON asg.Contractor_Id = s.Contractor_Id
# LEFT JOIN invoice_subcontractor_hold_join h ON i.Invoice_Id = h.Invoice_Id AND h.Contractor_Id = s.Contractor_Id
# LEFT JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# WHERE s.Contractor_Id = %s
# ORDER BY i.PMC_No ASC;
# """
# cursor.execute(query, (contractor_id,))
# invoices = cursor.fetchall()
#
# # cursor.callproc('GetInvoicesWithHoldByContId', [contractor_id])
# # invoices = next(cursor.stored_results()).fetchall()
#
# gst_query = """select pmc_no,invoice_no,basic_amount,final_amount from gst_release where pmc_no in
# (select distinct pmc_no from assign_subcontractors where Contractor_Id= %s ) ORDER BY pmc_no ASC """
# cursor.execute(gst_query, (contractor_id,))
# gst_rel = cursor.fetchall()
#
# # cursor.callproc('GetGstReleaseBycontId', [contractor_id])
# # gst_rel = next(cursor.stored_results()).fetchall()
#
# pay_query = """select pmc_no,invoice_no,Payment_Amount,TDS_Payment_Amount,Total_amount,utr from payment where pmc_no in
# (select distinct pmc_no from assign_subcontractors where Contractor_Id=%s ) ORDER BY pmc_no ASC """
# cursor.execute(pay_query, (contractor_id,))
# payments = cursor.fetchall()
# # cursor.callproc('GetPaymentsByContId', [contractor_id])
# # payments = next(cursor.stored_results()).fetchall()
#
# 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')
#
# except Exception as e:
# print(f"Error fetching contractor report: {e}")
# return "An error occurred while fetching contractor report", 500
#
# finally:
# cursor.close()
# connection.close()
#
# return render_template('subcontractor_report.html', contInfo=contInfo, contractor_id=contractor_id,
# invoices=invoices,
# hold_types=hold_types, gst_rel=gst_rel, payments=payments, total=total,
# current_date=current_date)
# @app.route('/contractor_report/<int:contractor_id>')
# def contractor_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True, buffered=True)
# try:
# # Fetch contractor details
# cursor.execute("""
# SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# s.Mobile_No, s.GST_Registration_Type, s.GST_No,s.PAN_No,s.Email,s.Address
# FROM subcontractors s
# LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE s.Contractor_Id = %s
# """, (contractor_id,))
# contInfo = cursor.fetchone()
# # # Fetch distinct hold types present in invoices for the contractor
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_types = cursor.fetchall()
# # cursor.callproc('GetDistinctHoldTypesInInvoicesByContractor', [contractor_id])
# # for result in cursor.stored_results():
# # hold_types = result.fetchall()
# # # Fetch all invoices for the contractor, with optional hold information
# query = """
# SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# i.After_Debit_Amount, i.Amount, i.GST_Amount, i.TDS_Amount, i.SD_Amount,
# i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
# i.Final_Amount, h.hold_amount, ht.hold_type
# FROM assign_subcontractors asg
# INNER JOIN villages v ON asg.Village_Id = v.Village_Id
# INNER JOIN invoice i ON i.Village_Id = v.Village_Id AND i.PMC_No = asg.PMC_No
# LEFT JOIN subcontractors s ON asg.Contractor_Id = s.Contractor_Id
# LEFT JOIN invoice_subcontractor_hold_join h ON i.Invoice_Id = h.Invoice_Id AND h.Contractor_Id = asg.Contractor_Id
# LEFT JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# WHERE asg.Contractor_Id = %s
# ORDER BY i.PMC_No ASC;
# """
# cursor.execute(query, (contractor_id,))
# invoices = cursor.fetchall()
# # cursor.callproc("GetInvoicesWithHoldInfoByContractor", [contractor_id])
# # for result in cursor.stored_results():
# # invoices = result.fetchall()
# gst_query = """select pmc_no,invoice_no,basic_amount,final_amount from gst_release where pmc_no in
# (select distinct pmc_no from assign_subcontractors where Contractor_Id= %s ) ORDER BY pmc_no ASC """
# cursor.execute(gst_query, (contractor_id,))
# gst_rel = cursor.fetchall()
# # cursor.callproc('GetGSTReleasesByContractor', [contractor_id])
# # for result in cursor.stored_results():
# # gst_rel = result.fetchall()
# pay_query = """select pmc_no,invoice_no,Payment_Amount,TDS_Payment_Amount,Total_amount,utr from payment where pmc_no in
# (select distinct pmc_no from assign_subcontractors where Contractor_Id=%s ) ORDER BY pmc_no ASC """
# cursor.execute(pay_query, (contractor_id,))
# payments = cursor.fetchall()
# # cursor.callproc('GetPaymentsByContractor', [contractor_id])
# # for result in cursor.stored_results():
# # payments = result.fetchall()
# 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')
# except Exception as e:
# print(f"Error fetching contractor report: {e}")
# return "An error occurred while fetching contractor report", 500
# finally:
# cursor.close()
# connection.close()
# return render_template('subcontractor_report.html', contInfo=contInfo, contractor_id=contractor_id,
# invoices=invoices,
# hold_types=hold_types, gst_rel=gst_rel, payments=payments, total=total,
# current_date=current_date)
from flask import render_template
from datetime import datetime
import config
@app.route('/contractor_report/<int:contractor_id>')
def contractor_report(contractor_id):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
try:
# Contractor details
cursor.execute("""
SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
FROM subcontractors s
LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
LEFT JOIN districts d ON b.District_id = d.District_id
LEFT JOIN states st ON d.State_Id = st.State_Id
WHERE s.Contractor_Id = %s
""", (contractor_id,))
contInfo = cursor.fetchone()
# Hold types
cursor.execute("""
SELECT DISTINCT ht.hold_type_id, ht.hold_type
FROM invoice_subcontractor_hold_join h
JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
WHERE h.Contractor_Id = %s
""", (contractor_id,))
hold_types = cursor.fetchall()
# Invoices
cursor.execute("""
SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
i.After_Debit_Amount, i.Amount, i.GST_Amount, i.TDS_Amount, i.SD_Amount,
i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
i.Final_Amount, h.hold_amount, ht.hold_type
FROM assign_subcontractors asg
INNER JOIN villages v ON asg.Village_Id = v.Village_Id
INNER JOIN invoice i ON i.Village_Id = v.Village_Id AND i.PMC_No = asg.PMC_No
LEFT JOIN invoice_subcontractor_hold_join h ON i.Invoice_Id = h.Invoice_Id AND h.Contractor_Id = asg.Contractor_Id
LEFT JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
WHERE asg.Contractor_Id = %s
ORDER BY i.PMC_No ASC
""", (contractor_id,))
invoices = cursor.fetchall()
# GST Release
cursor.execute("""
SELECT gr.pmc_no, gr.invoice_no, gr.basic_amount, gr.final_amount
FROM gst_release gr
INNER JOIN (
SELECT DISTINCT i.PMC_No, i.Invoice_No
FROM invoice i
JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
WHERE a.Contractor_Id = %s
) x ON gr.pmc_no = x.PMC_No AND gr.invoice_no = x.Invoice_No
ORDER BY gr.pmc_no ASC
""", (contractor_id,))
gst_rel = cursor.fetchall()
#Hold
# Hold Release
cursor.execute("SELECT * FROM hold_release WHERE Contractor_Id=%s", (contractor_id,))
hold_release = cursor.fetchall()
print(hold_release)
#Credit Note
cursor.execute("select * from credit_note where Contractor_Id=%s",(contractor_id,))
credit_note=cursor.fetchall()
print(credit_note)
# Payments (include valid matches and payments with pmc_no but invoice_no is NULL)
cursor.execute("""
SELECT p.pmc_no, p.invoice_no, p.Payment_Amount, p.TDS_Payment_Amount, p.Total_amount, p.utr
FROM payment p
WHERE
EXISTS (
SELECT 1
FROM invoice i
JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
WHERE a.Contractor_Id = %s
AND i.PMC_No = p.pmc_no AND i.Invoice_No = p.invoice_no
)
OR (
p.invoice_no IS NULL
AND EXISTS (
SELECT 1
FROM assign_subcontractors a
WHERE a.Contractor_Id = %s
AND a.PMC_No = p.pmc_no
)
)
ORDER BY p.pmc_no ASC
""", (contractor_id, contractor_id))
payments = cursor.fetchall()
# 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')
except Exception as e:
print(f"Error fetching contractor report: {e}")
return "An error occurred while fetching contractor report", 500
finally:
cursor.close()
connection.close()
return render_template('subcontractor_report.html',
contInfo=contInfo,
contractor_id=contractor_id,
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)
# -----------------------------download contractor report---------------------------------
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
#
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
#
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
#
# try:
# # Fetch Contractor Details
# # cursor.execute("""
# # SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# # s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# # FROM subcontractors s
# # LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# # LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# # LEFT JOIN districts d ON b.District_id = d.District_id
# # LEFT JOIN states st ON d.State_Id = st.State_Id
# # WHERE s.Contractor_Id = %s
# # """, (contractor_id,))
# # contInfo = cursor.fetchone()
# cursor.callproc('GetContractorInfoById', [contractor_id])
# contInfo = next(cursor.stored_results()).fetchone()
#
# if not contInfo:
# return "No contractor found", 404
#
# # Fetch distinct hold types present for the contractor
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_types = cursor.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# # Fetch Invoices & GST Releases
# cursor.execute("""
# SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount,
# g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no,
# g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount
# FROM invoice i
# LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No
# LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no
# WHERE asg.Contractor_Id = %s
# """, (contractor_id,))
# invoices = cursor.fetchall()
#
# # Fetch Hold Amounts separately
# cursor.execute("""
# SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_amounts = cursor.fetchall()
#
# # Create a mapping of invoice_id to hold amounts by type
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# # Extract unique PMC numbers for payments
# pmc_numbers = tuple(set(inv['PMC_No'] for inv in invoices if inv['PMC_No'] is not None))
#
# # Fetch all Payments for the PMC numbers (including those with null invoice_no)
# payments_map = {}
# extra_payments_map = {} # Now using a map to organize extra payments by PMC
# if pmc_numbers:
# # First get payments with invoice_no
# query = f"""
# SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# FROM payment
# WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# AND invoice_no IS NOT NULL
# ORDER BY pmc_no, invoice_no
# """
# cursor.execute(query, pmc_numbers)
# payments = cursor.fetchall()
#
# # Organize payments by PMC No & Invoice No
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# if key not in payments_map:
# payments_map[key] = []
# payments_map[key].append(pay)
#
# # Then get extra payments (null invoice_no) and organize by PMC
# query = f"""
# SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# FROM payment
# WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# AND invoice_no IS NULL
# ORDER BY pmc_no
# """
# cursor.execute(query, pmc_numbers)
# extra_payments = cursor.fetchall()
#
# for pay in extra_payments:
# if pay['pmc_no'] not in extra_payments_map:
# extra_payments_map[pay['pmc_no']] = []
# extra_payments_map[pay['pmc_no']].append(pay)
#
# # Create Excel workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
#
# # Write Contractor Details
# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""])
# sheet.append(
# ["Contractor Name", contInfo["Contractor_Name"], " ", "GST No", contInfo["GST_No"], " ", "GST Type",
# contInfo["GST_Registration_Type"]])
# sheet.append(
# ["State", contInfo["State_Name"], " ", "PAN No", contInfo["PAN_No"], " ", "Address", contInfo["Address"]])
# sheet.append(["District", contInfo["District_Name"], " ", "Mobile No", contInfo["Mobile_No"]])
# sheet.append(["Block", contInfo["Block_Name"], " ", "Email", contInfo["Email"]])
# sheet.append([])
#
# # Table Headers - include all hold types as separate columns
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
#
# hold_headers = [ht['hold_type'] for ht in hold_types]
#
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
#
# sheet.append(base_headers + hold_headers + payment_headers)
#
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set() # Track which payments we've processed
#
# # Process invoices grouped by PMC No
# pmc_groups = {}
# for inv in invoices:
# pmc_no = inv["PMC_No"]
# if pmc_no not in pmc_groups:
# pmc_groups[pmc_no] = []
# pmc_groups[pmc_no].append(inv)
#
# # Process each PMC group separately
# for pmc_no, pmc_invoices in pmc_groups.items():
# # Process all invoices for this PMC first
# for inv in pmc_invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get((pmc_no, invoice_no), [])
#
# # Process invoice row with first payment (if exists)
# if (pmc_no, invoice_no) not in seen_invoices:
# seen_invoices.add((pmc_no, invoice_no))
# first_payment = payments[0] if len(payments) > 0 else None
#
# # Base invoice data
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
#
# # Add hold amounts for each hold type
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
#
# # Add payment information
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
#
# sheet.append(row)
#
# if first_payment:
# payment_id = f"{pmc_no}-{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process GST release if exists (only if we have a matching GST record)
# if inv["gst_pmc_no"] and (inv["gst_pmc_no"], inv["gst_invoice_no"]) not in seen_gst_notes:
# seen_gst_notes.add((inv["gst_pmc_no"], inv["gst_invoice_no"]))
#
# # Find the payment that matches this GST release
# gst_payment = None
# for payment in payments[1:]: # Skip first payment (already used for invoice)
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
#
# # GST release row
# row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for GST release
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
#
# sheet.append(row)
#
# if gst_payment:
# payment_id = f"{pmc_no}-{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process remaining payments as extra payments
# for payment in payments[1:]:
# payment_id = f"{pmc_no}-{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", payment['invoice_no'],
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for extra payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Process extra payments for this PMC (null invoice_no) immediately after the PMC's invoices
# if pmc_no in extra_payments_map:
# for payment in extra_payments_map[pmc_no]:
# payment_id = f"{pmc_no}-null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", "",
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for null invoice payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# workbook.save(output_file)
# workbook.close()
# finally:
# cursor.close()
# connection.close()
# from openpyxl.styles import Font
#
# # Initialize totals
# total_basic_amount = 0
# total_tds_amount = 0
# total_sd_amount = 0
# total_on_commission = 0
# total_hold_amount = 0
# total_final_amount = 0
# total_total_amount = 0 # Sum of all Total Amounts from payment data
#
# # Iterate through rows to calculate totals
# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0) # Basic_Amount
# total_tds_amount += float(row[11] or 0) # TDS_Amount
# total_sd_amount += float(row[12] or 0) # SD_Amount
# total_on_commission += float(row[13] or 0) # On_Commission
# total_final_amount += float(row[-5] or 0) # Final_Amount
# total_total_amount += float(row[-2] or 0) # Total_Amount (from payments)
#
# # Sum of hold amounts (dynamically calculated based on hold_headers)
# hold_start_col = len(base_headers) # First column where hold types start
# hold_end_col = hold_start_col + len(hold_headers) # Last column where hold types end
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
#
# except ValueError:
# continue # Skip if non-numeric values cause errors
#
# # Append the totals row at the bottom of the sheet
# totals_row = [
# "TOTAL", "", "", "", "", "", # Empty values for non-numeric columns
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", "", # Empty GST SD Amount
# ]
#
# # Add empty placeholders for hold types
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
#
# # Add totals for Final Amount and Total Amount
# totals_row += [
# total_final_amount, "", "", total_total_amount, "" # UTR column remains empty
# ]
#
# # Append totals row to sheet
# sheet.append([])
# sheet.append(totals_row)
#
# # Make the totals row bold
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# sheet.append([])
# sheet.append([])
# sheet.append([])
# sheet.append([])
# sheet.append(["This is Generated by LCEPL Application.."])
#
# # Save workbook after modifications
# workbook.save(output_file)
# workbook.close()
#
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
from openpyxl import Workbook
# # Download report by contractor id
# # Download report by contractor id
from flask import send_from_directory
import config
# -----------------------PMC Report---------------------------
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
# try:
# # Fetch Contractor Details
# cursor.execute("""
# SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# FROM subcontractors s
# LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE s.Contractor_Id = %s
# """, (contractor_id,))
# contInfo = cursor.fetchone()
# if not contInfo:
# return "No contractor found", 404
# # # Fetch distinct hold types present for the contractor
# # cursor.execute("""
# # SELECT DISTINCT ht.hold_type_id, ht.hold_type
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_id,))
# # hold_types = cursor.fetchall()
# cursor.callproc('GetDistinctHoldTypesByContractor', [contractor_id])
# for result in cursor.stored_results():
# hold_types = result.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
# # # Fetch Invoices & GST Releases
# # cursor.execute("""
# # SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# # i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# # i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# # i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount,
# # g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no,
# # g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount
# # FROM invoice i
# # LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No
# # LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# # LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no
# # WHERE asg.Contractor_Id = %s
# # """, (contractor_id,))
# # invoices = cursor.fetchall()
# cursor.callproc('GetInvoicesAndGSTReleasesByContractor', [contractor_id])
# for result in cursor.stored_results():
# invoices = result.fetchall()
# # # Fetch Hold Amounts separately
# # cursor.execute("""
# # SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_id,))
# # hold_amounts = cursor.fetchall()
# cursor.callproc('GetHoldAmountsByContractors', [contractor_id])
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
# # Create a mapping of invoice_id to hold amounts by type
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
# # Extract unique PMC numbers for payments
# pmc_numbers = tuple(set(inv['PMC_No'] for inv in invoices if inv['PMC_No'] is not None))
# # Fetch all Payments for the PMC numbers (including those with null invoice_no)
# payments_map = {}
# extra_payments_map = {} # Now using a map to organize extra payments by PMC
# if pmc_numbers:
# # First get payments with invoice_no
# query = f"""
# SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# FROM payment
# WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# AND invoice_no IS NOT NULL
# ORDER BY pmc_no, invoice_no
# """
# cursor.execute(query, pmc_numbers)
# payments = cursor.fetchall()
# # Organize payments by PMC No & Invoice No
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# if key not in payments_map:
# payments_map[key] = []
# payments_map[key].append(pay)
# # Then get extra payments (null invoice_no) and organize by PMC
# query = f"""
# SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# FROM payment
# WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# AND invoice_no IS NULL
# ORDER BY pmc_no
# """
# cursor.execute(query, pmc_numbers)
# extra_payments = cursor.fetchall()
# for pay in extra_payments:
# if pay['pmc_no'] not in extra_payments_map:
# extra_payments_map[pay['pmc_no']] = []
# extra_payments_map[pay['pmc_no']].append(pay)
# # Create Excel workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
# # Write Contractor Details
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["State", contInfo["State_Name"]])
# sheet.append(["District", contInfo["District_Name"]])
# sheet.append(["Block", contInfo["Block_Name"]])
# sheet.append(["Mobile No", contInfo["Mobile_No"]])
# sheet.append(["GST Type", contInfo["GST_Registration_Type"]])
# sheet.append(["GST No", contInfo["GST_No"]])
# sheet.append(["PAN No", contInfo["PAN_No"]])
# sheet.append(["Email", contInfo["Email"]])
# sheet.append(["Address", contInfo["Address"]])
# sheet.append([])
# # Table Headers - include all hold types as separate columns
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
# hold_headers = [ht['hold_type'] for ht in hold_types]
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# sheet.append(base_headers + hold_headers + payment_headers)
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set() # Track which payments we've processed
# # Process invoices grouped by PMC No
# pmc_groups = {}
# for inv in invoices:
# pmc_no = inv["PMC_No"]
# if pmc_no not in pmc_groups:
# pmc_groups[pmc_no] = []
# pmc_groups[pmc_no].append(inv)
# # Process each PMC group separately
# for pmc_no, pmc_invoices in pmc_groups.items():
# # Process all invoices for this PMC first
# for inv in pmc_invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get((pmc_no, invoice_no), [])
# # Process invoice row with first payment (if exists)
# if (pmc_no, invoice_no) not in seen_invoices:
# seen_invoices.add((pmc_no, invoice_no))
# first_payment = payments[0] if len(payments) > 0 else None
# # Base invoice data
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
# # Add hold amounts for each hold type
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
# # Add payment information
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
# sheet.append(row)
# if first_payment:
# payment_id = f"{pmc_no}-{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# # Process GST release if exists (only if we have a matching GST record)
# if inv["gst_pmc_no"] and (inv["gst_pmc_no"], inv["gst_invoice_no"]) not in seen_gst_notes:
# seen_gst_notes.add((inv["gst_pmc_no"], inv["gst_invoice_no"]))
# # Find the payment that matches this GST release
# gst_payment = None
# for payment in payments[1:]: # Skip first payment (already used for invoice)
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
# # GST release row
# row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for GST release
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
# sheet.append(row)
# if gst_payment:
# payment_id = f"{pmc_no}-{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# # Process remaining payments as extra payments
# for payment in payments[1:]:
# payment_id = f"{pmc_no}-{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", payment['invoice_no'],
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for extra payments
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# # Process extra payments for this PMC (null invoice_no) immediately after the PMC's invoices
# if pmc_no in extra_payments_map:
# for payment in extra_payments_map[pmc_no]:
# payment_id = f"{pmc_no}-null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", "",
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for null invoice payments
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# workbook.save(output_file)
# workbook.close()
# finally:
# cursor.close()
# connection.close()
# from openpyxl.styles import Font
# # Initialize totals
# total_basic_amount = 0
# total_tds_amount = 0
# total_sd_amount = 0
# total_on_commission = 0
# total_hold_amount = 0
# total_final_amount = 0
# total_total_amount = 0 # Sum of all Total Amounts from payment data
# # Iterate through rows to calculate totals
# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0) # Basic_Amount
# total_tds_amount += float(row[11] or 0) # TDS_Amount
# total_sd_amount += float(row[12] or 0) # SD_Amount
# total_on_commission += float(row[13] or 0) # On_Commission
# total_final_amount += float(row[-5] or 0) # Final_Amount
# total_total_amount += float(row[-2] or 0) # Total_Amount (from payments)
# # Sum of hold amounts (dynamically calculated based on hold_headers)
# hold_start_col = len(base_headers) # First column where hold types start
# hold_end_col = hold_start_col + len(hold_headers) # Last column where hold types end
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
# except ValueError:
# continue # Skip if non-numeric values cause errors
# # Append the totals row at the bottom of the sheet
# totals_row = [
# "TOTAL", "", "", "", "", "", # Empty values for non-numeric columns
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", "", # Empty GST SD Amount
# ]
# # Add empty placeholders for hold types
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
# # Add totals for Final Amount and Total Amount
# totals_row += [
# total_final_amount, "", "", total_total_amount, "" # UTR column remains empty
# ]
# # Append totals row to sheet
# sheet.append([])
# sheet.append(totals_row)
# # Make the totals row bold
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
# # Save workbook after modifications
# workbook.save(output_file)
# workbook.close()
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
#
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
#
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
#
# try:
# # Fetch Contractor Details
# # cursor.execute("""
# # SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# # s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# # FROM subcontractors s
# # LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# # LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# # LEFT JOIN districts d ON b.District_id = d.District_id
# # LEFT JOIN states st ON d.State_Id = st.State_Id
# # WHERE s.Contractor_Id = %s
# # """, (contractor_id,))
# # contInfo = cursor.fetchone()
# cursor.callproc('GetContractorInfoId', [contractor_id])
# for result in cursor.stored_results():
# contInfo = result.fetchone()
#
# if not contInfo:
# return "No contractor found", 404
#
# # # Fetch distinct hold types present for the contractor
# # cursor.execute("""
# # SELECT DISTINCT ht.hold_type_id, ht.hold_type
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_id,))
# # hold_types = cursor.fetchall()
# cursor.callproc('GetDistinctHoldTypesByContractor', [contractor_id])
#
# for result in cursor.stored_results():
# hold_types = result.fetchall()
#
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# # # Fetch Invoices & GST Releases
# # cursor.execute("""
# # SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# # i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# # i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# # i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount,
# # g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no,
# # g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount
# # FROM invoice i
# # LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No
# # LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# # LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no
# # WHERE asg.Contractor_Id = %s
# # """, (contractor_id,))
# # invoices = cursor.fetchall()
# cursor.callproc('GetInvoicesAndGSTReleasesByContractor', [contractor_id])
#
# for result in cursor.stored_results():
# invoices = result.fetchall()
#
# # # Fetch Hold Amounts separately
# # cursor.execute("""
# # SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_id,))
# # hold_amounts = cursor.fetchall()
# cursor.callproc('GetHoldAmountsByContractors', [contractor_id])
#
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
#
# # Create a mapping of invoice_id to hold amounts by type
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# # Extract unique PMC numbers for payments
# pmc_numbers = tuple(set(inv['PMC_No'] for inv in invoices if inv['PMC_No'] is not None))
#
# # Fetch all Payments for the PMC numbers (including those with null invoice_no)
# payments_map = {}
# extra_payments_map = {} # Now using a map to organize extra payments by PMC
# if pmc_numbers:
# # # First get payments with invoice_no
# # query = f"""
# # SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# # FROM payment
# # WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# # AND invoice_no IS NOT NULL
# # ORDER BY pmc_no, invoice_no
# # """
# # cursor.execute(query, pmc_numbers)
# # payments = cursor.fetchall()
#
# pmc_list_str = ','.join(str(pmc) for pmc in pmc_numbers)
# # e.g. 'PMC001,PMC002,PMC003'
# cursor.callproc('GetPaymentsByPmcNosWithInvoice', [pmc_list_str])
#
# for result in cursor.stored_results():
# payments = result.fetchall()
#
# print("payments by procedure:", payments)
# # Organize payments by PMC No & Invoice No
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# if key not in payments_map:
# payments_map[key] = []
# payments_map[key].append(pay)
#
# # Then get extra payments (null invoice_no) and organize by PMC
# # query = f"""
# # SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# # FROM payment
# # WHERE pmc_no IN ({','.join(['%s'] * len(pmc_numbers))})
# # AND invoice_no IS NULL
# # ORDER BY pmc_no
# # """
# # cursor.execute(query, pmc_numbers)
# # extra_payments = cursor.fetchall()
# pmc_list_str = ','.join(str(pmc) for pmc in pmc_numbers)
# cursor.callproc('GetExtraPaymentsByPmcNos', [pmc_list_str])
#
# for result in cursor.stored_results():
# extra_payments = result.fetchall()
# print(
# "-----------------------------------------------------------------------------------------Extra Payment---------------------------------------------------------------")
# print("Extra payment from produre", extra_payments)
#
# for pay in extra_payments:
# if pay['pmc_no'] not in extra_payments_map:
# extra_payments_map[pay['pmc_no']] = []
# extra_payments_map[pay['pmc_no']].append(pay)
# print(
# "-----------------------------------------------------------------------------------------Extra Payment MAP --------------------------------------------------------------")
# print("Extra payment MAp from produre", extra_payments_map)
# # Create Excel workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
#
# # Write Contractor Details
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["State", contInfo["State_Name"]])
# sheet.append(["District", contInfo["District_Name"]])
# sheet.append(["Block", contInfo["Block_Name"]])
# sheet.append(["Mobile No", contInfo["Mobile_No"]])
# sheet.append(["GST Type", contInfo["GST_Registration_Type"]])
# sheet.append(["GST No", contInfo["GST_No"]])
# sheet.append(["PAN No", contInfo["PAN_No"]])
# sheet.append(["Email", contInfo["Email"]])
# sheet.append(["Address", contInfo["Address"]])
# sheet.append([])
#
# # Table Headers - include all hold types as separate columns
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
#
# hold_headers = [ht['hold_type'] for ht in hold_types]
#
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
#
# sheet.append(base_headers + hold_headers + payment_headers)
#
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set() # Track which payments we've processed
#
# # Process invoices grouped by PMC No
# pmc_groups = {}
# for inv in invoices:
# pmc_no = inv["PMC_No"]
# if pmc_no not in pmc_groups:
# pmc_groups[pmc_no] = []
# pmc_groups[pmc_no].append(inv)
#
# # Process each PMC group separately
# for pmc_no, pmc_invoices in pmc_groups.items():
# # Process all invoices for this PMC first
# for inv in pmc_invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get((pmc_no, invoice_no), [])
#
# # Process invoice row with first payment (if exists)
# if (pmc_no, invoice_no) not in seen_invoices:
# seen_invoices.add((pmc_no, invoice_no))
# first_payment = payments[0] if len(payments) > 0 else None
#
# # Base invoice data
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
#
# # Add hold amounts for each hold type
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
#
# # Add payment information
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
#
# sheet.append(row)
#
# if first_payment:
# payment_id = f"{pmc_no}-{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process GST release if exists (only if we have a matching GST record)
# if inv["gst_pmc_no"] and (inv["gst_pmc_no"], inv["gst_invoice_no"]) not in seen_gst_notes:
# seen_gst_notes.add((inv["gst_pmc_no"], inv["gst_invoice_no"]))
#
# # Find the payment that matches this GST release
# gst_payment = None
# for payment in payments[1:]: # Skip first payment (already used for invoice)
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
#
# # GST release row
# row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for GST release
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
#
# sheet.append(row)
#
# if gst_payment:
# payment_id = f"{pmc_no}-{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process remaining payments as extra payments
# for payment in payments[1:]:
# payment_id = f"{pmc_no}-{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", payment['invoice_no'],
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for extra payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Process extra payments for this PMC (null invoice_no) immediately after the PMC's invoices
# if pmc_no in extra_payments_map:
# for payment in extra_payments_map[pmc_no]:
# payment_id = f"{pmc_no}-null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", "",
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for null invoice payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# workbook.save(output_file)
# workbook.close()
# finally:
# cursor.close()
# connection.close()
# from openpyxl.styles import Font
#
# # Initialize totals
# total_basic_amount = 0
# total_tds_amount = 0
# total_sd_amount = 0
# total_on_commission = 0
# total_hold_amount = 0
# total_final_amount = 0
# total_total_amount = 0 # Sum of all Total Amounts from payment data
#
# # Iterate through rows to calculate totals
# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0) # Basic_Amount
# total_tds_amount += float(row[11] or 0) # TDS_Amount
# total_sd_amount += float(row[12] or 0) # SD_Amount
# total_on_commission += float(row[13] or 0) # On_Commission
# total_final_amount += float(row[-5] or 0) # Final_Amount
# total_total_amount += float(row[-2] or 0) # Total_Amount (from payments)
#
# # Sum of hold amounts (dynamically calculated based on hold_headers)
# hold_start_col = len(base_headers) # First column where hold types start
# hold_end_col = hold_start_col + len(hold_headers) # Last column where hold types end
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
#
# except ValueError:
# continue # Skip if non-numeric values cause errors
#
# # Append the totals row at the bottom of the sheet
# totals_row = [
# "TOTAL", "", "", "", "", "", # Empty values for non-numeric columns
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", "", # Empty GST SD Amount
# ]
# if hold_headers:
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
#
# # Add empty placeholders for hold types
# # totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
#
# # Add totals for Final Amount and Total Amount
# totals_row += [
# total_final_amount, "", "", total_total_amount, "" # UTR column remains empty
# ]
#
# # Append totals row to sheet
# sheet.append([])
# sheet.append(totals_row)
# total_hold_amount=Decimal('0.00')
# for d in invoices:
# total_hold_amount = total_hold_amount + d.get('SD_Amount', Decimal('0.00')) + d.get('On_Commission',
# Decimal(
# '0.00')) + d.get(
# 'Hydro_Testing', Decimal('0.00'))
# #new coded for summary chart added
# for data in hold_amounts:
# total_hold_amount = total_hold_amount + data.get('hold_amount', Decimal('0.00'))
# print("Total Hold Amount after adding the hold amount ", total_hold_amount)
#
#
# from datetime import datetime
#
# # Add payment information
#
# # Get today's date with weekday
# today_date = datetime.today().strftime('%A, %Y-%m-%d')
#
# # Add headers (optional)
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
#
# # Add your values
# sheet.append(["Advance/Surplus", str( total_final_amount - total_total_amount)])
# sheet.append(["Total Hold Amount", str(total_hold_amount)])
# sheet.append(["Amount With TDS", str(total_tds_amount)])
# #new added code end here for summary chart
#
# # Make the totals row bold
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# # Save workbook after modifications
# workbook.save(output_file)
# workbook.close()
#
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
from flask import send_from_directory
import os
import openpyxl
from openpyxl.styles import Font, PatternFill
from decimal import Decimal
from datetime import datetime
from flask import send_from_directory
import os
import openpyxl
from openpyxl.styles import Font, PatternFill
from decimal import Decimal
from datetime import datetime
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
# try:
# # Fetch Contractor Info
# cursor.callproc('GetContractorInfoId', [contractor_id])
# for result in cursor.stored_results():
# contInfo = result.fetchone()
# if not contInfo:
# return "No contractor found", 404
# # Fetch Hold Types
# cursor.callproc('GetDistinctHoldTypesByContractor', [contractor_id])
# for result in cursor.stored_results():
# hold_types = result.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
# # Fetch Invoices
# cursor.callproc('GetInvoicesAndGSTReleasesByContractor', [contractor_id])
# for result in cursor.stored_results():
# invoices = result.fetchall()
# # Fetch Hold Amounts
# cursor.callproc('GetHoldAmountsByContractors', [contractor_id])
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
# # Extract PMC numbers
# pmc_numbers = tuple(set(inv['PMC_No'] for inv in invoices if inv['PMC_No'] is not None))
# payments_map = {}
# extra_payments_map = {}
# if pmc_numbers:
# pmc_list_str = ','.join(str(pmc) for pmc in pmc_numbers)
# cursor.callproc('GetPaymentsByPmcNosWithInvoice', [pmc_list_str])
# for result in cursor.stored_results():
# payments = result.fetchall()
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# payments_map.setdefault(key, []).append(pay)
# cursor.callproc('GetExtraPaymentsByPmcNos', [pmc_list_str])
# for result in cursor.stored_results():
# extra_payments = result.fetchall()
# for pay in extra_payments:
# extra_payments_map.setdefault(pay['pmc_no'], []).append(pay)
# # Create Excel Workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
# # Write Contractor Info
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["State", contInfo["State_Name"]])
# sheet.append(["District", contInfo["District_Name"]])
# sheet.append(["Block", contInfo["Block_Name"]])
# sheet.append(["Mobile No", contInfo["Mobile_No"]])
# sheet.append(["GST Type", contInfo["GST_Registration_Type"]])
# sheet.append(["GST No", contInfo["GST_No"]])
# sheet.append(["PAN No", contInfo["PAN_No"]])
# sheet.append(["Email", contInfo["Email"]])
# sheet.append(["Address", contInfo["Address"]])
# sheet.append([])
# # Table 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"]
# hold_headers = [ht['hold_type'] for ht in hold_types]
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# all_headers = base_headers + hold_headers + payment_headers
# sheet.append(all_headers)
# # Style the headers
# header_fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
# header_font = Font(bold=True)
# for cell in sheet[sheet.max_row]:
# cell.font = header_font
# cell.fill = header_fill
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set()
# pmc_groups = {}
# for inv in invoices:
# pmc_groups.setdefault(inv["PMC_No"], []).append(inv)
# for pmc_no, pmc_invoices in pmc_groups.items():
# for inv in pmc_invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get((pmc_no, invoice_no), [])
# if (pmc_no, invoice_no) not in seen_invoices:
# seen_invoices.add((pmc_no, 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:
# row.append(invoice_holds.get(ht_id, ""))
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
# sheet.append(row)
# if first_payment:
# payment_id = f"{pmc_no}-{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# # GST Notes
# if inv["gst_pmc_no"] and (inv["gst_pmc_no"], inv["gst_invoice_no"]) not in seen_gst_notes:
# seen_gst_notes.add((inv["gst_pmc_no"], inv["gst_invoice_no"]))
# gst_payment = None
# for payment in payments[1:]:
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
# row = [pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
# sheet.append(row)
# if gst_payment:
# payment_id = f"{pmc_no}-{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# for payment in payments[1:]:
# payment_id = f"{pmc_no}-{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [pmc_no, "", "", "", "", payment['invoice_no'], "", "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# # Extra payments (null invoice)
# for payment in extra_payments_map.get(pmc_no, []):
# payment_id = f"{pmc_no}-null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [pmc_no, "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# # Totals Calculation
# total_basic_amount = total_tds_amount = total_sd_amount = total_on_commission = 0
# total_hold_amount = total_final_amount = total_total_amount = 0
# for row in sheet.iter_rows(min_row=13, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0)
# total_tds_amount += float(row[11] or 0)
# total_sd_amount += float(row[12] or 0)
# total_on_commission += float(row[13] or 0)
# total_final_amount += float(row[-5] or 0)
# total_total_amount += float(row[-2] or 0)
# hold_start_col = len(base_headers)
# hold_end_col = hold_start_col + len(hold_headers)
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
# except ValueError:
# continue
# # Totals Row
# sheet.append([])
# totals_row = [
# "TOTAL", "", "", "", "", "", total_basic_amount, "", "", "", "", total_tds_amount,
# total_sd_amount, total_on_commission, "", ""
# ]
# if total_hold_amount:
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
# totals_row += [total_final_amount, "", "", total_total_amount, ""]
# sheet.append(totals_row)
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
# # Summary Section
# today_date = datetime.today().strftime('%A, %Y-%m-%d')
# sheet.append([])
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
# sheet.append(["Advance/Surplus", str(total_final_amount - total_total_amount)])
# sheet.append(["Total Hold Amount", str(Decimal(total_hold_amount))])
# sheet.append(["Amount With TDS", str(total_tds_amount)])
# # Auto adjust column widths
# for col in sheet.columns:
# max_length = 0
# col_letter = openpyxl.utils.get_column_letter(col[0].column)
# for cell in col:
# try:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# except:
# pass
# sheet.column_dimensions[col_letter].width = max_length + 2
# workbook.save(output_file)
# workbook.close()
# finally:
# cursor.close()
# connection.close()
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
from flask import send_from_directory
from decimal import Decimal
from datetime import datetime
import os
import openpyxl
from openpyxl.styles import Font, PatternFill
import config
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True, buffered=True)
#
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
# os.makedirs(output_folder, exist_ok=True)
#
# try:
# # Contractor Info
# cursor.execute("""
# SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# FROM subcontractors s
# LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE s.Contractor_Id = %s
# """, (contractor_id,))
# contInfo = cursor.fetchone()
# if not contInfo:
# return "No contractor found", 404
#
# # Hold Types
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_types = cursor.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# # Invoices
# cursor.execute("""
# SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
# i.Final_Amount, i.Invoice_Id
# FROM assign_subcontractors asg
# INNER JOIN villages v ON asg.Village_Id = v.Village_Id
# INNER JOIN invoice i ON i.Village_Id = v.Village_Id AND i.PMC_No = asg.PMC_No
# WHERE asg.Contractor_Id = %s
# ORDER BY i.PMC_No ASC, i.Invoice_No ASC
# """, (contractor_id,))
# invoices = cursor.fetchall()
#
# # Hold Amounts
# cursor.execute("""
# SELECT h.Invoice_Id, h.hold_type_id, h.hold_amount
# FROM invoice_subcontractor_hold_join h
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_amounts = cursor.fetchall()
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# # GST Release query - Modified to get all GST releases for the contractor
# cursor.execute("""
# SELECT gr.pmc_no, gr.invoice_no, gr.basic_amount, gr.final_amount
# FROM gst_release gr
# WHERE gr.pmc_no IN (
# SELECT DISTINCT a.PMC_No
# FROM assign_subcontractors a
# WHERE a.Contractor_Id = %s
# )
# ORDER BY gr.pmc_no ASC
# """, (contractor_id,))
# gst_rel = cursor.fetchall()
#
# gst_rel_map = {}
# for gr in gst_rel:
# invoice_nos = []
# if gr['invoice_no']:
# if ',' in gr['invoice_no']:
# invoice_nos = [x.strip() for x in gr['invoice_no'].replace(' ', '').split(',')]
# elif '&' in gr['invoice_no']:
# invoice_nos = [x.strip() for x in gr['invoice_no'].split('&')]
# else:
# invoice_nos = [gr['invoice_no'].strip()]
#
# # Map each individual invoice to the GST release
# for inv_no in invoice_nos:
# key = (gr['pmc_no'], inv_no)
# if key not in gst_rel_map:
# gst_rel_map[key] = {
# 'basic_amount': gr['basic_amount'],
# 'final_amount': gr['final_amount'],
# 'invoice_no': gr['invoice_no'],
# 'all_invoice_nos': invoice_nos
# }
#
# # Also map the combined invoice number if it's a combined release
# if len(invoice_nos) > 1:
# combined_key = (gr['pmc_no'], gr['invoice_no'])
# gst_rel_map[combined_key] = {
# 'basic_amount': gr['basic_amount'],
# 'final_amount': gr['final_amount'],
# 'invoice_no': gr['invoice_no'],
# 'all_invoice_nos': invoice_nos
# }
#
# # GST Release Payments - Updated to include Payment_Amount and TDS_Payment_Amount
# cursor.execute("""
# SELECT p.pmc_no, p.invoice_no, p.Payment_Amount, p.TDS_Payment_Amount, p.Total_amount, p.utr
# FROM payment p
# JOIN gst_release gr ON p.pmc_no = gr.pmc_no AND (
# p.invoice_no = gr.invoice_no
# OR (gr.invoice_no LIKE '%,%' AND FIND_IN_SET(p.invoice_no, REPLACE(gr.invoice_no, ' ', '')) > 0)
# OR (gr.invoice_no LIKE '%&%' AND (
# p.invoice_no = SUBSTRING_INDEX(gr.invoice_no, '&', 1)
# OR p.invoice_no = SUBSTRING_INDEX(gr.invoice_no, '&', -1)
# OR (gr.invoice_no LIKE '%&%&%' AND p.invoice_no = SUBSTRING_INDEX(SUBSTRING_INDEX(gr.invoice_no, '&', 2), '&', -1))
# ))
# )
# WHERE gr.pmc_no IN (
# SELECT DISTINCT i.PMC_No
# FROM invoice i
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
# WHERE a.Contractor_Id = %s
# )
# AND p.Total_amount != 0
# """, (contractor_id,))
# gst_payments = cursor.fetchall()
#
# gst_payment_map = {}
# for pay in gst_payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# gst_payment_map[key] = {
# 'Payment_Amount': pay['Payment_Amount'],
# 'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
# 'Total_amount': pay['Total_amount'],
# 'utr': pay['utr']
# }
# # Also map to the combined invoice number if it exists
# for gr_key, gr_val in gst_rel_map.items():
# if pay['pmc_no'] == gr_key[0] and pay['invoice_no'] in gr_val['all_invoice_nos']:
# combined_key = (pay['pmc_no'], gr_val['invoice_no'])
# gst_payment_map[combined_key] = {
# 'Payment_Amount': pay['Payment_Amount'],
# 'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
# 'Total_amount': pay['Total_amount'],
# 'utr': pay['utr']
# }
#
# # Invoice Payments - Updated to include Payment_Amount and TDS_Payment_Amount
# cursor.execute("""
# SELECT p.pmc_no, p.invoice_no, p.Payment_Amount, p.TDS_Payment_Amount, p.Total_amount, p.utr
# FROM payment p
# INNER JOIN (
# SELECT DISTINCT i.PMC_No, i.Invoice_No
# FROM invoice i
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
# WHERE a.Contractor_Id = %s
# ) x ON p.pmc_no = x.PMC_No AND p.invoice_no = x.Invoice_No
# ORDER BY p.pmc_no ASC
# """, (contractor_id,))
# payments = cursor.fetchall()
# payments_map = {}
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# payments_map.setdefault(key, []).append({
# 'Payment_Amount': pay['Payment_Amount'],
# 'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
# 'Total_amount': pay['Total_amount'],
# 'utr': pay['utr']
# })
#
# # Extra Payments (no invoice_no) - Updated to include Payment_Amount and TDS_Payment_Amount
# cursor.execute("""
# SELECT pmc_no, Payment_Amount, TDS_Payment_Amount, Total_amount, utr
# FROM payment
# WHERE (invoice_no IS NULL OR invoice_no = '')
# AND Total_amount != 0
# AND pmc_no IS NOT NULL
# """)
# extra_payments_raw = cursor.fetchall()
# extra_payments_map = {}
# for pay in extra_payments_raw:
# extra_payments_map.setdefault(pay['pmc_no'], []).append({
# 'Payment_Amount': pay['Payment_Amount'],
# 'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
# 'Total_amount': pay['Total_amount'],
# 'utr': pay['utr']
# })
#
# # Credit Note
# # Credit Note Fetch
# cursor.execute("""
# SELECT PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount,
# GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No
# FROM credit_note
# WHERE Contractor_Id = %s
# """, (contractor_id,))
#
# credit_notes = cursor.fetchall()
#
# # Build map by (PMC_No, Invoice_No)
# credit_note_map = {}
# for cn in credit_notes:
# key = (cn["PMC_No"], cn["Invoice_No"]) # Use correct casing!
# credit_note_map.setdefault(key, []).append(cn)
#
# # Track already appended credit notes
# appended_credit_keys = set()
#
# # Excel Workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
#
# # Contractor Info section
# for field, value in contInfo.items():
# sheet.append([field.replace("_", " "), value])
# 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]
# # Updated payment headers to include Payment Amount and TDS Payment
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# all_headers = base_headers + hold_headers + payment_headers
# sheet.append(all_headers)
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
# cell.fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
#
# # Track which GST releases we've already processed
# processed_gst_releases = set()
#
# # Data Rows
# for inv in invoices:
# pmc_no = inv["PMC_No"]
# invoice_no = inv["Invoice_No"]
#
# 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:
# row.append(invoice_holds.get(ht_id, ""))
#
# payment = payments_map.get((pmc_no, invoice_no), [None])[0]
# row += [
# inv["Final_Amount"],
# payment["Payment_Amount"] if payment else "",
# payment["TDS_Payment_Amount"] if payment else "",
# payment["Total_amount"] if payment else "",
# payment["utr"] if payment and payment["utr"] else ""
# ]
# sheet.append(row)
#
# # GST Release row - only show after the last invoice in the sequence
# if (pmc_no, invoice_no) in gst_rel_map:
# gr = gst_rel_map[(pmc_no, invoice_no)]
#
# # Check if this is the last invoice in the sequence
# if invoice_no == gr['all_invoice_nos'][-1] and gr['invoice_no'] not in processed_gst_releases:
# processed_gst_releases.add(gr['invoice_no'])
#
# # Find matching payment if it exists
# gst_payment = gst_payment_map.get((pmc_no, gr['invoice_no']), None)
# if not gst_payment:
# # Try matching with individual invoice numbers
# for inv_no in gr['all_invoice_nos']:
# if (pmc_no, inv_no) in gst_payment_map:
# gst_payment = gst_payment_map[(pmc_no, inv_no)]
# break
#
# gst_row = [
# pmc_no, "", "", "GST Release Note", "", gr["invoice_no"],
# gr["basic_amount"], "", "", "", "", "", "", "", "", ""
# ]
# gst_row += ["" for _ in hold_headers]
# gst_row += [
# gr["final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["utr"] if gst_payment and gst_payment.get("utr") else ""
# ]
# sheet.append(gst_row)
#
# # Extra Payments (once per pmc_no)
# if pmc_no in extra_payments_map:
# for extra in extra_payments_map[pmc_no]:
# extra_row = [
# pmc_no, "", "", " ", "", "", "", "", "", "", "", "", "", "", "", ""
# ]
# extra_row += ["" for _ in hold_headers]
# extra_row += [
# "",
# extra['Payment_Amount'],
# extra['TDS_Payment_Amount'],
# extra['Total_amount'],
# extra['utr'] if extra['utr'] else ""
# ]
# sheet.append(extra_row)
# del extra_payments_map[pmc_no]
#
# # Now add any GST releases that weren't associated with any invoices
# for gr in gst_rel:
# if gr['invoice_no'] not in processed_gst_releases:
# pmc_no = gr['pmc_no']
# invoice_no = gr['invoice_no']
#
# # Find matching payment if it exists
# gst_payment = gst_payment_map.get((pmc_no, invoice_no), None)
#
# gst_row = [
# pmc_no, "", "", "GST Release Note", "", invoice_no,
# gr["basic_amount"], "", "", "", "", "", "", "", "", ""
# ]
# gst_row += ["" for _ in hold_headers]
# gst_row += [
# gr["final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["utr"] if gst_payment and gst_payment.get("utr") else ""
# ]
# sheet.append(gst_row)
# processed_gst_releases.add(invoice_no)
# # Credit Note row(s)
#
# # Track already appended credit notes
# appended_credit_keys = set()
#
# # While writing invoices
# key = (pmc_no, invoice_no)
# if key in credit_note_map and key not in appended_credit_keys:
# for cn in credit_note_map[key]:
# credit_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", ""), "", "",
# "",
# "", ""
# ]
# credit_row += ["" for _ in hold_headers]
# credit_row += [
# cn.get("Final_Amount", ""),
# cn.get("Total_Amount", ""),
# cn.get("UTR", "")
# ]
# sheet.append(credit_row)
# appended_credit_keys.add(key)
#
# # Totals row (NO UTR totals, UTR blank)
# total_basic_amount = total_tds_amount = total_sd_amount = total_on_commission = 0
# total_final_amount = total_total_amount = total_hold_amount = 0
# for row in sheet.iter_rows(min_row=13, max_row=sheet.max_row, values_only=True):
# try:
# desc = row[3]
# if desc == " ":
# total_total_amount += float(row[-2] or 0)
# continue
#
# total_basic_amount += float(row[6] or 0)
# total_tds_amount += float(row[11] or 0)
# total_sd_amount += float(row[12] or 0)
# total_on_commission += float(row[13] or 0)
# total_final_amount += float(row[-5] or 0) # Changed to -5 for Final Amount
# total_total_amount += float(row[-2] or 0)
# # sum holds
# total_hold_amount += sum(
# float(row[i] or 0) for i in range(len(base_headers), len(base_headers) + len(hold_headers)))
# except Exception:
# pass
#
# totals_row = [
# "Total", "", "", "", "", "", total_basic_amount, "", "", "", "", total_tds_amount,
# total_sd_amount, total_on_commission, "", ""
# ]
# totals_row += [total_hold_amount for _ in hold_headers]
# totals_row += [total_final_amount, "", "", total_total_amount,
# ""] # UTR blank, Payment Amount and TDS Payment blank
# sheet.append([])
# sheet.append(totals_row)
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# # Summary Section
# today_date = datetime.today().strftime('%A, %Y-%m-%d')
# sheet.append([])
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
# sheet.append(["Advance/Surplus", str(total_final_amount - total_total_amount)])
# sheet.append(["Total Hold Amount", str(Decimal(total_hold_amount))])
# sheet.append(["Amount With TDS", str(total_tds_amount)])
#
# # Auto adjust column widths
# for col in sheet.columns:
# max_length = 0
# col_letter = openpyxl.utils.get_column_letter(col[0].column)
# for cell in col:
# try:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# except:
# pass
# sheet.column_dimensions[col_letter].width = max_length + 2
#
# workbook.save(output_file)
# workbook.close()
#
# finally:
# cursor.close()
# connection.close()
#
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
@app.route('/download_report/<int:contractor_id>')
def download_report(contractor_id):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
output_folder = "static/download"
output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
os.makedirs(output_folder, exist_ok=True)
try:
# Contractor Info
cursor.execute("""
SELECT DISTINCT s.Contractor_Name,
st.State_Name,
d.District_Name,
b.Block_Name,
s.Mobile_No,
s.GST_Registration_Type,
s.GST_No,
s.PAN_No,
s.Email,
s.Address
FROM subcontractors s
LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
LEFT JOIN districts d ON b.District_id = d.District_id
LEFT JOIN states st ON d.State_Id = st.State_Id
WHERE s.Contractor_Id = %s
""", (contractor_id,))
contInfo = cursor.fetchone()
if not contInfo:
return "No contractor found", 404
# Hold Types
cursor.execute("""
SELECT DISTINCT ht.hold_type_id, ht.hold_type
FROM invoice_subcontractor_hold_join h
JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
WHERE h.Contractor_Id = %s
""", (contractor_id,))
hold_types = cursor.fetchall()
hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
# Invoices
cursor.execute("""
SELECT DISTINCT i.PMC_No,
v.Village_Name,
i.Work_Type,
i.Invoice_Details,
i.Invoice_Date,
i.Invoice_No,
i.Basic_Amount,
i.Debit_Amount,
i.After_Debit_Amount,
i.GST_Amount,
i.Amount,
i.TDS_Amount,
i.SD_Amount,
i.On_Commission,
i.Hydro_Testing,
i.GST_SD_Amount,
i.Final_Amount,
i.Invoice_Id
FROM assign_subcontractors asg
INNER JOIN villages v ON asg.Village_Id = v.Village_Id
INNER JOIN invoice i ON i.Village_Id = v.Village_Id AND i.PMC_No = asg.PMC_No
WHERE asg.Contractor_Id = %s
ORDER BY i.PMC_No ASC, i.Invoice_No ASC
""", (contractor_id,))
invoices = cursor.fetchall()
# Hold Amounts
cursor.execute("""
SELECT h.Invoice_Id, h.hold_type_id, h.hold_amount
FROM invoice_subcontractor_hold_join h
JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
WHERE h.Contractor_Id = %s
""", (contractor_id,))
hold_amounts = cursor.fetchall()
hold_data = {}
for h in hold_amounts:
hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
# GST Release query - Modified to get all GST releases for the contractor
cursor.execute("""
SELECT gr.pmc_no, gr.invoice_no, gr.basic_amount, gr.final_amount
FROM gst_release gr
WHERE gr.pmc_no IN (SELECT DISTINCT a.PMC_No
FROM assign_subcontractors a
WHERE a.Contractor_Id = %s)
ORDER BY gr.pmc_no ASC
""", (contractor_id,))
gst_rel = cursor.fetchall()
gst_rel_map = {}
for gr in gst_rel:
invoice_nos = []
if gr['invoice_no']:
if ',' in gr['invoice_no']:
invoice_nos = [x.strip() for x in gr['invoice_no'].replace(' ', '').split(',')]
elif '&' in gr['invoice_no']:
invoice_nos = [x.strip() for x in gr['invoice_no'].split('&')]
else:
invoice_nos = [gr['invoice_no'].strip()]
# Map each individual invoice to the GST release
for inv_no in invoice_nos:
key = (gr['pmc_no'], inv_no)
if key not in gst_rel_map:
gst_rel_map[key] = {
'basic_amount': gr['basic_amount'],
'final_amount': gr['final_amount'],
'invoice_no': gr['invoice_no'],
'all_invoice_nos': invoice_nos
}
# Also map the combined invoice number if it's a combined release
if len(invoice_nos) > 1:
combined_key = (gr['pmc_no'], gr['invoice_no'])
gst_rel_map[combined_key] = {
'basic_amount': gr['basic_amount'],
'final_amount': gr['final_amount'],
'invoice_no': gr['invoice_no'],
'all_invoice_nos': invoice_nos
}
# GST Release Payments - Updated to include Payment_Amount and TDS_Payment_Amount
cursor.execute("""
SELECT p.pmc_no, p.invoice_no, p.Payment_Amount, p.TDS_Payment_Amount, p.Total_amount, p.utr
FROM payment p
JOIN gst_release gr ON p.pmc_no = gr.pmc_no AND (
p.invoice_no = gr.invoice_no
OR
(gr.invoice_no LIKE '%,%' AND FIND_IN_SET(p.invoice_no, REPLACE(gr.invoice_no, ' ', '')) > 0)
OR (gr.invoice_no LIKE '%&%' AND (
p.invoice_no = SUBSTRING_INDEX(gr.invoice_no, '&', 1)
OR p.invoice_no = SUBSTRING_INDEX(gr.invoice_no, '&', -1)
OR (gr.invoice_no LIKE '%&%&%' AND
p.invoice_no = SUBSTRING_INDEX(SUBSTRING_INDEX(gr.invoice_no, '&', 2), '&', -1))
))
)
WHERE gr.pmc_no IN (SELECT DISTINCT i.PMC_No
FROM invoice i
JOIN assign_subcontractors a
ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
WHERE a.Contractor_Id = %s)
AND p.Total_amount != 0
""", (contractor_id,))
gst_payments = cursor.fetchall()
gst_payment_map = {}
for pay in gst_payments:
key = (pay['pmc_no'], pay['invoice_no'])
gst_payment_map[key] = {
'Payment_Amount': pay['Payment_Amount'],
'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
'Total_amount': pay['Total_amount'],
'utr': pay['utr']
}
# Also map to the combined invoice number if it exists
for gr_key, gr_val in gst_rel_map.items():
if pay['pmc_no'] == gr_key[0] and pay['invoice_no'] in gr_val['all_invoice_nos']:
combined_key = (pay['pmc_no'], gr_val['invoice_no'])
gst_payment_map[combined_key] = {
'Payment_Amount': pay['Payment_Amount'],
'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
'Total_amount': pay['Total_amount'],
'utr': pay['utr']
}
# Invoice Payments - Updated to include Payment_Amount and TDS_Payment_Amount
cursor.execute("""
SELECT p.pmc_no, p.invoice_no, p.Payment_Amount, p.TDS_Payment_Amount, p.Total_amount, p.utr
FROM payment p
INNER JOIN (SELECT DISTINCT i.PMC_No, i.Invoice_No
FROM invoice i
JOIN assign_subcontractors a
ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
WHERE a.Contractor_Id = %s) x
ON p.pmc_no = x.PMC_No AND p.invoice_no = x.Invoice_No
ORDER BY p.pmc_no ASC
""", (contractor_id,))
payments = cursor.fetchall()
payments_map = {}
for pay in payments:
key = (pay['pmc_no'], pay['invoice_no'])
payments_map.setdefault(key, []).append({
'Payment_Amount': pay['Payment_Amount'],
'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
'Total_amount': pay['Total_amount'],
'utr': pay['utr']
})
# Fetch hold_release data
cursor.execute("""
SELECT *
FROM hold_release
WHERE Contractor_Id = %s
""", (contractor_id,))
hold_releases = cursor.fetchall()
hold_release_map = {}
for hr in hold_releases:
invoice_nos = [i.strip() for i in str(hr['Invoice_No']).split('&')]
for inv_no in invoice_nos:
key = (hr['PMC_No'], inv_no)
hold_release_map.setdefault(key, []).append(hr)
# Credit Note
# Credit Note Fetch
cursor.execute("""
SELECT PMC_No,
Invoice_Details,
Basic_Amount,
Debit_Amount,
After_Debit_Amount,
GST_Amount,
Amount,
Final_Amount,
Payment_Amount,
Total_Amount,
UTR,
Invoice_No
FROM credit_note
WHERE Contractor_Id = %s
""", (contractor_id,))
credit_notes = cursor.fetchall()
# Build map by (PMC_No, Invoice_No)
credit_note_map = {}
for cn in credit_notes:
key = (cn["PMC_No"], cn["Invoice_No"]) # Use correct casing!
credit_note_map.setdefault(key, []).append(cn)
print("Credit_note_map:",credit_note_map)
# Track already appended credit notes
appended_credit_keys = set()
#print("appended_credit_keys",appended_credit_keys)
# Extra Payments (no invoice_no) - Updated to include Payment_Amount and TDS_Payment_Amount
cursor.execute("""
SELECT pmc_no, Payment_Amount, TDS_Payment_Amount, Total_amount, utr
FROM payment
WHERE (invoice_no IS NULL OR invoice_no = '')
AND Total_amount != 0
AND pmc_no IS NOT NULL
""")
extra_payments_raw = cursor.fetchall()
extra_payments_map = {}
for pay in extra_payments_raw:
extra_payments_map.setdefault(pay['pmc_no'], []).append({
'Payment_Amount': pay['Payment_Amount'],
'TDS_Payment_Amount': pay['TDS_Payment_Amount'],
'Total_amount': pay['Total_amount'],
'utr': pay['utr']
})
# Excel Workbook
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "Contractor Report"
# Contractor Info section
for field, value in contInfo.items():
sheet.append([field.replace("_", " "), value])
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]
# Updated payment headers to include Payment Amount and TDS Payment
payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
all_headers = base_headers + hold_headers + payment_headers
sheet.append(all_headers)
for cell in sheet[sheet.max_row]:
cell.font = Font(bold=True)
cell.fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
# Track which GST releases we've already processed
processed_gst_releases = set()
# Data Rows
for inv in invoices:
pmc_no = inv["PMC_No"]
invoice_no = inv["Invoice_No"]
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:
row.append(invoice_holds.get(ht_id, ""))
payment = payments_map.get((pmc_no, invoice_no), [None])[0]
row += [
inv["Final_Amount"],
payment["Payment_Amount"] if payment else "",
payment["TDS_Payment_Amount"] if payment else "",
payment["Total_amount"] if payment else "",
payment["utr"] if payment and payment["utr"] else ""
]
sheet.append(row)
# GST Release row - only show after the last invoice in the sequence
if (pmc_no, invoice_no) in gst_rel_map:
gr = gst_rel_map[(pmc_no, invoice_no)]
# Check if this is the last invoice in the sequence
if invoice_no == gr['all_invoice_nos'][-1] and gr['invoice_no'] not in processed_gst_releases:
processed_gst_releases.add(gr['invoice_no'])
# Find matching payment if it exists
gst_payment = gst_payment_map.get((pmc_no, gr['invoice_no']), None)
if not gst_payment:
# Try matching with individual invoice numbers
for inv_no in gr['all_invoice_nos']:
if (pmc_no, inv_no) in gst_payment_map:
gst_payment = gst_payment_map[(pmc_no, inv_no)]
break
gst_row = [
pmc_no, "", "", "GST Release Note", "", gr["invoice_no"],
gr["basic_amount"], "", "", "", "", "", "", "", "", ""
]
gst_row += ["" for _ in hold_headers]
gst_row += [
gr["final_amount"],
gst_payment["Payment_Amount"] if gst_payment else "",
gst_payment["TDS_Payment_Amount"] if gst_payment else "",
gst_payment["Total_amount"] if gst_payment else "",
gst_payment["utr"] if gst_payment and gst_payment.get("utr") else ""
]
sheet.append(gst_row)
# Hold Release row(s)
if (pmc_no, invoice_no) in hold_release_map:
for hr in hold_release_map[(pmc_no, invoice_no)]:
hr_row = [
pmc_no, "", "", hr["Invoice_Details"], "", hr["Invoice_No"],
hr.get("Basic_Amount", ""), "", "", "", "", "", "", "", "", "","",""
]
hr_row += ["" for _ in hold_headers]
hr_row += [
hr.get("Final_Amount", ""),
hr.get("Total_Amount", ""),
hr.get("UTR", "")
]
sheet.append(hr_row)
# Extra Payments (once per pmc_no)
if pmc_no in extra_payments_map:
for extra in extra_payments_map[pmc_no]:
extra_row = [
pmc_no, "", "", " ", "", "", "", "", "", "", "", "", "", "", "", ""
]
extra_row += ["" for _ in hold_headers]
extra_row += [
"",
extra['Payment_Amount'],
extra['TDS_Payment_Amount'],
extra['Total_amount'],
extra['utr'] if extra['utr'] else ""
]
sheet.append(extra_row)
del extra_payments_map[pmc_no]
# Now add any GST releases that weren't associated with any invoices
for gr in gst_rel:
if gr['invoice_no'] not in processed_gst_releases:
pmc_no = gr['pmc_no']
invoice_no = gr['invoice_no']
# Find matching payment if it exists
gst_payment = gst_payment_map.get((pmc_no, invoice_no), None)
gst_row = [
pmc_no, "", "", "GST Release Note", "", invoice_no,
gr["basic_amount"], "", "", "", "", "", "", "", "", ""
]
gst_row += ["" for _ in hold_headers]
gst_row += [
gr["final_amount"],
gst_payment["Payment_Amount"] if gst_payment else "",
gst_payment["TDS_Payment_Amount"] if gst_payment else "",
gst_payment["Total_amount"] if gst_payment else "",
gst_payment["utr"] if gst_payment and gst_payment.get("utr") else ""
]
sheet.append(gst_row)
processed_gst_releases.add(invoice_no)
# Credit Note row(s)
# # Track already appended credit notes
appended_credit_keys = set()
sheet.append(row)
# Add this block right here:
key = (pmc_no, invoice_no)
if key in credit_note_map and key not in appended_credit_keys:
for cn in credit_note_map[key]:
credit_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", ""), "", "", "",
"", ""
]
credit_row += ["" for _ in hold_headers]
credit_row += [
cn.get("Final_Amount", ""), "", "", cn.get("Total_Amount", ""),
cn.get("UTR", "")
]
sheet.append(credit_row)
appended_credit_keys.add(key)
# Totals row (NO UTR totals, UTR blank)
total_basic_amount = total_tds_amount = total_sd_amount = total_on_commission = 0
total_final_amount = total_total_amount = total_hold_amount = 0
for row in sheet.iter_rows(min_row=13, max_row=sheet.max_row, values_only=True):
try:
desc = row[3]
if desc == " ":
total_total_amount += float(row[-2] or 0)
continue
total_basic_amount += float(row[6] or 0)
total_tds_amount += float(row[11] or 0)
total_sd_amount += float(row[12] or 0)
total_on_commission += float(row[13] or 0)
total_final_amount += float(row[-5] or 0) # Changed to -5 for Final Amount
total_total_amount += float(row[-2] or 0)
# sum holds
total_hold_amount += sum(
float(row[i] or 0) for i in range(len(base_headers), len(base_headers) + len(hold_headers)))
except Exception:
pass
totals_row = [
"Total", "", "", "", "", "", total_basic_amount, "", "", "", "", total_tds_amount,
total_sd_amount, total_on_commission, "", ""
]
totals_row += [total_hold_amount for _ in hold_headers]
totals_row += [total_final_amount, "", "", total_total_amount,
""] # UTR blank, Payment Amount and TDS Payment blank
sheet.append([])
sheet.append(totals_row)
for cell in sheet[sheet.max_row]:
cell.font = Font(bold=True)
# Summary Section
today_date = datetime.today().strftime('%A, %Y-%m-%d')
sheet.append([])
sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
sheet.append(["Date", today_date])
sheet.append(["Description", "Amount"])
sheet.append(["Advance/Surplus", str(total_final_amount - total_total_amount)])
sheet.append(["Total Hold Amount", str(Decimal(total_hold_amount))])
sheet.append(["Amount With TDS", str(total_tds_amount)])
# Auto adjust column widths
for col in sheet.columns:
max_length = 0
col_letter = openpyxl.utils.get_column_letter(col[0].column)
for cell in col:
try:
if cell.value:
max_length = max(max_length, len(str(cell.value)))
except:
pass
sheet.column_dimensions[col_letter].width = max_length + 2
workbook.save(output_file)
workbook.close()
finally:
cursor.close()
connection.close()
return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True, buffered=True)
#
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
# os.makedirs(output_folder, exist_ok=True)
#
# try:
# # Contractor Info
# cursor.execute("""
# SELECT DISTINCT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# FROM subcontractors s
# LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# LEFT JOIN districts d ON b.District_id = d.District_id
# LEFT JOIN states st ON d.State_Id = st.State_Id
# WHERE s.Contractor_Id = %s
# """, (contractor_id,))
# contInfo = cursor.fetchone()
# if not contInfo:
# return "No contractor found", 404
#
# # Hold Types
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_types = cursor.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# # Invoices
# cursor.execute("""
# SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
# i.Final_Amount, i.Invoice_Id
# FROM assign_subcontractors asg
# INNER JOIN villages v ON asg.Village_Id = v.Village_Id
# INNER JOIN invoice i ON i.Village_Id = v.Village_Id AND i.PMC_No = asg.PMC_No
# WHERE asg.Contractor_Id = %s
# ORDER BY i.PMC_No ASC
# """, (contractor_id,))
# invoices = cursor.fetchall()
#
# # Hold Amounts
# cursor.execute("""
# SELECT h.Invoice_Id, h.hold_type_id, h.hold_amount
# FROM invoice_subcontractor_hold_join h
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# WHERE h.Contractor_Id = %s
# """, (contractor_id,))
# hold_amounts = cursor.fetchall()
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# # GST Release
# cursor.execute("""
# SELECT gr.pmc_no, gr.invoice_no, gr.basic_amount, gr.final_amount
# FROM gst_release gr
# INNER JOIN (
# SELECT DISTINCT i.PMC_No, i.Invoice_No
# FROM invoice i
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
# WHERE a.Contractor_Id = %s
# ) x ON gr.pmc_no = x.PMC_No AND gr.invoice_no = x.Invoice_No
# ORDER BY gr.pmc_no ASC
# """, (contractor_id,))
# gst_rel = cursor.fetchall()
# gst_rel_map = {(gr['pmc_no'], gr['invoice_no']): gr for gr in gst_rel}
# #
#
#
#
#
#
# #Credit Note
# # Credit Note Fetch
# cursor.execute("""
# SELECT PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount,
# GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No
# FROM credit_note
# WHERE Contractor_Id = %s
# """, (contractor_id,))
#
# credit_notes = cursor.fetchall()
#
# # Build map by (PMC_No, Invoice_No)
# credit_note_map = {}
# for cn in credit_notes:
# key = (cn["PMC_No"], cn["Invoice_No"]) # Use correct casing!
# credit_note_map.setdefault(key, []).append(cn)
#
# # Track already appended credit notes
# appended_credit_keys = set()
#
#
#
#
#
#
# # Invoice Payments
# cursor.execute("""
# SELECT p.pmc_no, p.invoice_no, p.Total_amount, p.utr
# FROM payment p
# INNER JOIN (
# SELECT DISTINCT i.PMC_No, i.Invoice_No
# FROM invoice i
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No AND i.Village_Id = a.Village_Id
# WHERE a.Contractor_Id = %s
# ) x ON p.pmc_no = x.PMC_No AND p.invoice_no = x.Invoice_No
# ORDER BY p.pmc_no ASC
# """, (contractor_id,))
# payments = cursor.fetchall()
# payments_map = {}
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# payments_map.setdefault(key, []).append(pay)
#
# #Credit Note Fetch-----------------------------------------------------------------------------
#
#
# # GST Release Payments
# cursor.execute("""
# SELECT p.pmc_no, p.invoice_no, p.Total_amount, p.utr
# FROM payment p
# INNER JOIN gst_release gr ON p.pmc_no = gr.pmc_no AND p.invoice_no = gr.invoice_no
# WHERE gr.invoice_no IS NOT NULL AND gr.pmc_no IS NOT NULL
# AND p.Total_amount != 0
# """)
# gst_payments = cursor.fetchall()
# gst_payment_map = {}
# for pay in gst_payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# gst_payment_map[key] = pay
#
# # Extra Payments (no invoice_no)
# cursor.execute("""
# SELECT pmc_no, Total_amount, utr
# FROM payment
# WHERE (invoice_no IS NULL OR invoice_no = '')
# AND Total_amount != 0
# AND pmc_no IS NOT NULL
# """)
# extra_payments_raw = cursor.fetchall()
# extra_payments_map = {}
# for pay in extra_payments_raw:
# extra_payments_map.setdefault(pay['pmc_no'], []).append(pay)
#
# # Excel Workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
#
# # Contractor Info section
# for field, value in contInfo.items():
# sheet.append([field.replace("_", " "), value])
# 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", "Total Paid", "UTR"]
# all_headers = base_headers + hold_headers + payment_headers
# sheet.append(all_headers)
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
# cell.fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
#
# # Data Rows
# for inv in invoices:
# pmc_no = inv["PMC_No"]
# invoice_no = inv["Invoice_No"]
#
# 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:
# row.append(invoice_holds.get(ht_id, ""))
#
# payment = payments_map.get((pmc_no, invoice_no), [None])[0]
# row += [
# inv["Final_Amount"],
# payment["Total_amount"] if payment else "",
# payment["utr"] if payment and payment["utr"] else "" # UTR only display, no total
# ]
# sheet.append(row)
#
# # GST Release row
# if (pmc_no, invoice_no) in gst_rel_map:
# gr = gst_rel_map[(pmc_no, invoice_no)]
# gst_payment = gst_payment_map.get((pmc_no, invoice_no))
#
# gst_row = [
# pmc_no, "", "", "GST Release Note", "", gr["invoice_no"],
# gr["basic_amount"], "", "", "", "", "", "", "", "", ""
# ]
# gst_row += ["" for _ in hold_headers]
# gst_row += [
# gr["final_amount"],
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["utr"] if gst_payment and gst_payment["utr"] else ""
# ]
# sheet.append(gst_row)
# # Credit Note row(s)
#
# # Track already appended credit notes
# appended_credit_keys = set()
#
# # While writing invoices
# key = (pmc_no, invoice_no)
# if key in credit_note_map and key not in appended_credit_keys:
# for cn in credit_note_map[key]:
# credit_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", ""), "", "", "",
# "", ""
# ]
# credit_row += ["" for _ in hold_headers]
# credit_row += [
# cn.get("Final_Amount", ""),
# cn.get("Total_Amount", ""),
# cn.get("UTR", "")
# ]
# sheet.append(credit_row)
# appended_credit_keys.add(key)
#
#
# # Extra Payments (once per pmc_no)
# if pmc_no in extra_payments_map:
# for extra in extra_payments_map[pmc_no]:
# extra_row = [
# pmc_no, "", "", " ", "", "", "", "", "", "", "", "", "", "", "", ""
# ]
# extra_row += ["" for _ in hold_headers]
# extra_row += ["", extra['Total_amount'], extra['utr'] if extra['utr'] else ""]
# sheet.append(extra_row)
# del extra_payments_map[pmc_no]
#
# # Totals row (NO UTR totals, UTR blank)
# total_basic_amount = total_tds_amount = total_sd_amount = total_on_commission = 0
# total_final_amount = total_total_amount = total_hold_amount = 0
# for row in sheet.iter_rows(min_row=13, max_row=sheet.max_row, values_only=True):
# try:
# desc = row[3]
# if desc == " ":
# total_total_amount += float(row[-2] or 0)
# continue
#
# total_basic_amount += float(row[6] or 0)
# total_tds_amount += float(row[11] or 0)
# total_sd_amount += float(row[12] or 0)
# total_on_commission += float(row[13] or 0)
# total_final_amount += float(row[-3] or 0)
# total_total_amount += float(row[-2] or 0)
# # sum holds
# total_hold_amount += sum(float(row[i] or 0) for i in range(len(base_headers), len(base_headers) + len(hold_headers)))
#
# # DO NOT sum UTR column (row[-1]) — skip it
# except Exception:
# pass
#
# totals_row = [
# "Total", "", "", "", "", "", total_basic_amount, "", "", "", "", total_tds_amount,
# total_sd_amount, total_on_commission, "", ""
# ]
# totals_row += [total_hold_amount for _ in hold_headers]
# totals_row += [total_final_amount, total_total_amount, ""] # UTR blank
# sheet.append([])
# sheet.append(totals_row)
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# # Summary Section
# today_date = datetime.today().strftime('%A, %Y-%m-%d')
# sheet.append([])
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
# sheet.append(["Advance/Surplus", str(total_final_amount - total_total_amount)])
# sheet.append(["Total Hold Amount", str(Decimal(total_hold_amount))])
# sheet.append(["Amount With TDS", str(total_tds_amount)])
#
# # Auto adjust column widths
# for col in sheet.columns:
# max_length = 0
# col_letter = openpyxl.utils.get_column_letter(col[0].column)
# for cell in col:
# try:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# except:
# pass
# sheet.column_dimensions[col_letter].width = max_length + 2
#
# workbook.save(output_file)
# workbook.close()
#
# finally:
# cursor.close()
# connection.close()
#
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
# @app.route('/download_report/<int:contractor_id>')
# def download_report(contractor_id):
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
#
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx")
#
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
#
# try:
# # Fetch Contractor Info
# cursor.callproc('GetContractorInfoId', [contractor_id])
# for result in cursor.stored_results():
# contInfo = result.fetchone()
# if not contInfo:
# return "No contractor found", 404
#
# # Fetch Hold Types
# cursor.callproc('GetDistinctHoldTypesByContractor', [contractor_id])
# for result in cursor.stored_results():
# hold_types = result.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# # Fetch Invoices
# cursor.callproc('GetInvoicesAndGSTReleasesByContractor', [contractor_id])
# for result in cursor.stored_results():
# invoices = result.fetchall()
#
# # Fetch Hold Amounts
# cursor.callproc('GetHoldAmountsByContractors', [contractor_id])
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# # Extract PMC numbers
# pmc_numbers = tuple(set(inv['PMC_No'] for inv in invoices if inv['PMC_No'] is not None))
#
# payments_map = {}
# extra_payments_map = {}
# if pmc_numbers:
# pmc_list_str = ','.join(str(pmc) for pmc in pmc_numbers)
# cursor.callproc('GetPaymentsByPmcNosWithInvoice', [pmc_list_str])
# for result in cursor.stored_results():
# payments = result.fetchall()
# for pay in payments:
# key = (pay['pmc_no'], pay['invoice_no'])
# payments_map.setdefault(key, []).append(pay)
#
# cursor.callproc('GetExtraPaymentsByPmcNos', [pmc_list_str])
# for result in cursor.stored_results():
# extra_payments = result.fetchall()
# for pay in extra_payments:
# extra_payments_map.setdefault(pay['pmc_no'], []).append(pay)
#
# # Create Excel Workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
#
# # Write Contractor Info
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["State", contInfo["State_Name"]])
# sheet.append(["District", contInfo["District_Name"]])
# sheet.append(["Block", contInfo["Block_Name"]])
# sheet.append(["Mobile No", contInfo["Mobile_No"]])
# sheet.append(["GST Type", contInfo["GST_Registration_Type"]])
# sheet.append(["GST No", contInfo["GST_No"]])
# sheet.append(["PAN No", contInfo["PAN_No"]])
# sheet.append(["Email", contInfo["Email"]])
# sheet.append(["Address", contInfo["Address"]])
# sheet.append([])
#
# # Table 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"]
# hold_headers = [ht['hold_type'] for ht in hold_types]
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# all_headers = base_headers + hold_headers + payment_headers
#
# sheet.append(all_headers)
#
# # Style the headers
# header_fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
# header_font = Font(bold=True)
# for cell in sheet[sheet.max_row]:
# cell.font = header_font
# cell.fill = header_fill
#
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set()
# pmc_groups = {}
# for inv in invoices:
# pmc_groups.setdefault(inv["PMC_No"], []).append(inv)
#
# for pmc_no, pmc_invoices in pmc_groups.items():
# for inv in pmc_invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get((pmc_no, invoice_no), [])
#
# if (pmc_no, invoice_no) not in seen_invoices:
# seen_invoices.add((pmc_no, 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:
# row.append(invoice_holds.get(ht_id, ""))
#
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
# sheet.append(row)
# if first_payment:
# payment_id = f"{pmc_no}-{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # GST Notes
# if inv["gst_pmc_no"] and (inv["gst_pmc_no"], inv["gst_invoice_no"]) not in seen_gst_notes:
# seen_gst_notes.add((inv["gst_pmc_no"], inv["gst_invoice_no"]))
# gst_payment = None
# for payment in payments[1:]:
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
# row = [pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
# sheet.append(row)
# if gst_payment:
# payment_id = f"{pmc_no}-{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# for payment in payments[1:]:
# payment_id = f"{pmc_no}-{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [pmc_no, "", "", "", "", payment['invoice_no'], "", "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Extra payments (null invoice)
# for payment in extra_payments_map.get(pmc_no, []):
# payment_id = f"{pmc_no}-null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [pmc_no, "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
# row += ["" for _ in hold_headers]
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Totals Calculation
# total_basic_amount = total_tds_amount = total_sd_amount = total_on_commission = 0
# total_hold_amount = total_final_amount = total_total_amount = 0
#
# for row in sheet.iter_rows(min_row=13, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0)
# total_tds_amount += float(row[11] or 0)
# total_sd_amount += float(row[12] or 0)
# total_on_commission += float(row[13] or 0)
# total_final_amount += float(row[-5] or 0)
# total_total_amount += float(row[-2] or 0)
# hold_start_col = len(base_headers)
# hold_end_col = hold_start_col + len(hold_headers)
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
# except ValueError:
# continue
#
# # Totals Row
# sheet.append([])
# totals_row = [
# "TOTAL", "", "", "", "", "", total_basic_amount, "", "", "", "", total_tds_amount,
# total_sd_amount, total_on_commission, "", ""
# ]
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
# totals_row += [total_final_amount, "", "", total_total_amount, ""]
# sheet.append(totals_row)
#
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# # Summary Section
# today_date = datetime.today().strftime('%A, %Y-%m-%d')
# sheet.append([])
# sheet.append(["Contractor Name", contInfo["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
# sheet.append(["Advance/Surplus", str(total_final_amount - total_total_amount)])
# sheet.append(["Total Hold Amount", str(Decimal(total_hold_amount))])
# sheet.append(["Amount With TDS", str(total_tds_amount)])
#
# # Auto adjust column widths
# for col in sheet.columns:
# max_length = 0
# col_letter = openpyxl.utils.get_column_letter(col[0].column)
# for cell in col:
# try:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# except:
# pass
# sheet.column_dimensions[col_letter].width = max_length + 2
#
# workbook.save(output_file)
# workbook.close()
#
# finally:
# cursor.close()
# connection.close()
#
# return send_from_directory(output_folder, f"Contractor_Report_{contractor_id}.xlsx", as_attachment=True)
# show report by pmc no
@app.route('/pmc_report/<pmc_no>')
def pmc_report(pmc_no):
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
try:
# 1. Fetch PMC info using stored procedure
# cursor.execute("""
# SELECT DISTINCT a.PMC_No, a.Village_Id, v.Village_Name, b.Block_Name,
# d.District_Name, s.State_Name, sc.Contractor_Id, sc.Contractor_Name,
# sc.Address, sc.Mobile_No, sc.PAN_No, sc.Email, sc.Gender,
# sc.GST_Registration_Type, sc.GST_No
# FROM assign_subcontractors a
# INNER JOIN villages v ON a.Village_Id = v.Village_Id
# INNER JOIN blocks b ON v.Block_Id = b.Block_Id
# INNER JOIN districts d ON b.District_id = d.District_id
# INNER JOIN states s ON d.State_Id = s.State_Id
# INNER JOIN subcontractors sc ON a.Contractor_Id = sc.Contractor_Id
# WHERE a.pmc_no = %s
# """, (pmc_no,))
# pmc_info = cursor.fetchone()
cursor.callproc("GetContractorInfoByPmcNo", (pmc_no,))
pmc_info = next(cursor.stored_results()).fetchone()
if not pmc_info:
return "No PMC found with this number", 404
# 2. Fetch hold types using stored procedure
# cursor.execute("""
# SELECT DISTINCT ht.hold_type_id, ht.hold_type
# FROM invoice_subcontractor_hold_join h
# JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# JOIN invoice i ON h.Invoice_Id = i.Invoice_Id
# JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No
# WHERE a.PMC_No = %s AND a.Contractor_Id = %s
# """, (pmc_no, pmc_info["Contractor_Id"]))
# hold_types = cursor.fetchall()
cursor.callproc("Get_pmc_hold_types", (pmc_no, pmc_info["Contractor_Id"]))
hold_types = next(cursor.stored_results()).fetchall()
hold_type_ids = [ht['hold_type_id'] for ht in hold_types]
# 3. Initialize invoice data
invoices = []
hold_amount_total = 0
# 4. Build invoice query
if hold_type_ids:
placeholders = ','.join(['%s'] * len(hold_type_ids))
query = f"""
SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
i.After_Debit_Amount, i.Amount, i.GST_Amount, i.TDS_Amount, i.SD_Amount,
i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount,
i.Final_Amount, h.hold_amount, ht.hold_type
FROM invoice i
LEFT JOIN villages v ON i.Village_Id = v.Village_Id
LEFT JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No
LEFT JOIN invoice_subcontractor_hold_join h ON i.Invoice_Id = h.Invoice_Id
LEFT JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
WHERE a.PMC_No = %s AND a.Contractor_Id = %s
AND (ht.hold_type_id IS NULL OR ht.hold_type_id IN ({placeholders}))
ORDER BY i.Invoice_Date, i.Invoice_No
"""
params = [pmc_no, pmc_info["Contractor_Id"]] + hold_type_ids
else:
query = """
SELECT DISTINCT i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
i.After_Debit_Amount, i.Amount, i.GST_Amount, i.TDS_Amount, i.SD_Amount,
i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount
FROM invoice i
LEFT JOIN villages v ON i.Village_Id = v.Village_Id
LEFT JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No
WHERE a.PMC_No = %s AND a.Contractor_Id = %s
ORDER BY i.Invoice_Date, i.Invoice_No
"""
params = [pmc_no, pmc_info["Contractor_Id"]]
cursor.execute(query, params)
invoices = cursor.fetchall()
if hold_type_ids:
hold_amount_total = sum(row.get('hold_amount', 0) or 0 for row in invoices)
# 5. Totals from invoices
total_invo_final = sum(row.get('Final_Amount', 0) or 0 for row in invoices)
# 6. GST release
cursor.execute("""
SELECT pmc_no, invoice_no, basic_amount, final_amount
FROM gst_release
WHERE pmc_no = %s
ORDER BY invoice_no ASC
""", (pmc_no,))
gst_rel = cursor.fetchall()
# gst_rel = cursor.fetchall()
# cursor.callproc('GetGSTReleaseByPMC', [pmc_no])
#
# # Fetch results
# for result in cursor.stored_results():
# gst_rel = result.fetchall()
total_gst_basic = sum(row.get('basic_amount', 0) or 0 for row in gst_rel)
total_gst_final = sum(row.get('final_amount', 0) or 0 for row in gst_rel)
# Hold Release Amount
cursor.execute("""select * from hold_release where pmc_no=%s""", (pmc_no,))
hold_release = cursor.fetchall()
print("All Hold Release ", hold_release)
# Credit Note
cursor.execute("select * from credit_note where pmc_no=%s", (pmc_no,))
credit_note = cursor.fetchall()
print(credit_note)
# 7. Payments
cursor.execute("""
SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, utr
FROM payment
WHERE pmc_no = %s
ORDER BY invoice_no ASC
""", (pmc_no,))
payments = cursor.fetchall()
# cursor.callproc('GetPaymentByPMC', [pmc_no])
#
# 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)
# 8. Final totals dictionary
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
}
except Exception as e:
print(f"Error fetching PMC report: {e}")
return "An error occurred while fetching PMC report", 500
finally:
cursor.close()
connection.close()
return render_template(
'pmc_report.html',
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
)
# # Download report by PMC No
# @app.route('/download_pmc_report/<pmc_no>')
# def download_pmc_report(pmc_no):
# connection = config.get_db_connection()
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx")
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
# cursor = connection.cursor(dictionary=True)
# try:
# # # Fetch Contractor Details using PMC No
# # cursor.execute("""
# # SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name,
# # s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address
# # FROM subcontractors s
# # LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id
# # LEFT JOIN villages v ON asg.Village_Id = v.Village_Id
# # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id
# # LEFT JOIN districts d ON b.District_id = d.District_id
# # LEFT JOIN states st ON d.State_Id = st.State_Id
# # WHERE asg.PMC_No = %s
# # """, (pmc_no,))
# # contractor_info = cursor.fetchone()
# cursor.callproc('GetContractorDetailsByPMC', [pmc_no])
# # Now fetch the result:
# for result in cursor.stored_results():
# contractor_info = result.fetchone()
# if not contractor_info:
# return "No contractor found for this PMC No", 404
# # # Fetch distinct hold types present for the contractor
# # cursor.execute("""
# # SELECT DISTINCT ht.hold_type_id, ht.hold_type
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_info["Contractor_Id"],))
# # hold_types = cursor.fetchall()
# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
# for result in cursor.stored_results():
# hold_types = result.fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
# # # # Fetch Invoices & GST Releases
# # cursor.execute("""
# # SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details,
# # i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount,
# # i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount,
# # i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount,
# # g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no,
# # g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount
# # FROM invoice i
# # LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No
# # LEFT JOIN villages v ON i.Village_Id = v.Village_Id
# # LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no
# # WHERE asg.PMC_No = %s
# # ORDER BY i.Invoice_Date, i.Invoice_No
# # """, (pmc_no,))
# # invoices = cursor.fetchall()
# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no])
# for result in cursor.stored_results():
# invoices = result.fetchall()
# print("pmc_report invoice data:",invoices)
# # cursor.callproc('GetInvoicesAndGSTReleasesByPMC', [pmc_no])
# # for result in cursor.stored_results():
# # invoices = result.fetchall()
# # # Fetch Hold Amounts separately
# # cursor.execute("""
# # SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount
# # FROM invoice_subcontractor_hold_join h
# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id
# # WHERE h.Contractor_Id = %s
# # """, (contractor_info["Contractor_Id"],))
# # hold_amounts = cursor.fetchall()
# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
# # Create a mapping of invoice_id to hold amounts by type
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
# # # Fetch all Payments for the PMC number
# # cursor.execute("""
# # SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR
# # FROM payment
# # WHERE pmc_no = %s
# # ORDER BY invoice_no
# # """, (pmc_no,))
# # all_payments = cursor.fetchall()
# cursor.callproc('GetAllPaymentsByPMC', [pmc_no])
# for result in cursor.stored_results():
# all_payments = result.fetchall()
# # Organize payments by Invoice No (both regular and GST release notes)
# payments_map = {}
# extra_payments = []
# for pay in all_payments:
# if pay['invoice_no']:
# key = pay['invoice_no']
# if key not in payments_map:
# payments_map[key] = []
# payments_map[key].append(pay)
# else:
# extra_payments.append(pay)
# # Create Excel workbook
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "PMC Report"
# # Write Contractor Details
# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""])
# sheet.append(
# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ",
# "GST Type", contractor_info["GST_Registration_Type"]])
# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address",
# contractor_info["Address"]])
# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]])
# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]])
# sheet.append([])
# # Table Headers - include all hold types as separate columns
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
# hold_headers = [ht['hold_type'] for ht in hold_types]
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# sheet.append(base_headers + hold_headers + payment_headers)
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set()
# # Process invoices
# for inv in invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get(invoice_no, [])
# # Process invoice row with first payment (if exists)
# if invoice_no not in seen_invoices:
# seen_invoices.add(invoice_no)
# first_payment = payments[0] if len(payments) > 0 else None
# # Base invoice data
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
# # Add hold amounts for each hold type
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
# # Add payment information
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
# sheet.append(row)
# if first_payment:
# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# # Process GST release if exists (only if we have a matching GST record)
# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes:
# seen_gst_notes.add(inv["gst_invoice_no"])
# # Find the payment that matches this GST release
# gst_payment = None
# for payment in payments[1:]: # Skip first payment (already used for invoice)
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
# # If no payment found in the invoice's payments, check all payments
# if not gst_payment:
# gst_payments = payments_map.get(inv["gst_invoice_no"], [])
# if gst_payments:
# gst_payment = gst_payments[0]
# # GST release row
# row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for GST release
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
# sheet.append(row)
# if gst_payment:
# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
# # Process remaining payments as extra payments
# for payment in payments[1:]:
# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", payment['invoice_no'],
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for extra payments
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# # Process extra payments (null invoice_no)
# for payment in extra_payments:
# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", "",
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
# # Empty holds for null invoice payments
# row += ["" for _ in hold_headers]
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
# # Calculate totals
# total_basic_amount = 0
# total_tds_amount = 0
# total_sd_amount = 0
# total_on_commission = 0
# total_hold_amount = 0
# total_final_amount = 0
# total_payment_amount = 0
# total_tds_payment_amount = 0
# total_total_paid = 0
# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0) # Basic_Amount
# total_tds_amount += float(row[11] or 0) # TDS_Amount
# total_sd_amount += float(row[12] or 0) # SD_Amount
# total_on_commission += float(row[13] or 0) # On_Commission
# total_final_amount += float(row[-5] or 0) # Final_Amount
# total_payment_amount += float(row[-4] or 0) # Payment_Amount
# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment
# total_total_paid += float(row[-2] or 0) # Total_Paid
# # Sum of hold amounts
# hold_start_col = len(base_headers)
# hold_end_col = hold_start_col + len(hold_headers)
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
# except (ValueError, IndexError, TypeError):
# continue
# # Append totals row
# totals_row = [
# "TOTAL", "", "", "", "", "",
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", "", # Empty GST SD Amount
# ]
# # Add hold totals
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
# # Add payment totals
# totals_row += [
# total_final_amount,
# total_payment_amount,
# total_tds_payment_amount,
# total_total_paid,
# "" # UTR column remains empty
# ]
# sheet.append([])
# sheet.append(totals_row)
# # Make totals row bold
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
# # Save Excel file
# workbook.save(output_file)
# workbook.close()
# finally:
# cursor.close()
# connection.close()
# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True)
# @app.route('/download_pmc_report/<pmc_no>')
# def download_pmc_report(pmc_no):
# connection = config.get_db_connection()
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx")
#
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
#
# cursor = connection.cursor(dictionary=True)
#
# try:
# cursor.callproc('GetContractorDetailsByPMC', [pmc_no])
#
# for result in cursor.stored_results():
# contractor_info = result.fetchone()
#
# if not contractor_info:
# return "No contractor found for this PMC No", 404
#
# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
#
# for result in cursor.stored_results():
# hold_types = result.fetchall()
#
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no])
#
# for result in cursor.stored_results():
# invoices = result.fetchall()
# total_tds=Decimal('0.00')
# final_amount=Decimal('0.00')
# # total_hold_amount=Decimal('0.00')
# for data in invoices:
# total_tds=total_tds+data.get('TDS_Amount',Decimal('0.00'))
# final_amount=final_amount+data.get('Final_Amount',Decimal('0.00'))
#
# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
#
# for result in cursor.stored_results():
# hold_amounts = result.fetchall()
#
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# cursor.callproc('GetAllPaymentsByPMC', [pmc_no])
#
# for result in cursor.stored_results():
# all_payments = result.fetchall()
# total_amount=Decimal('0.00')
# for d in all_payments:
# total_amount=total_amount+ d.get('Total_Amount',Decimal('0.00'))
# total_amount_paid= final_amount- total_amount;
# payments_map = {}
# extra_payments = []
# for pay in all_payments:
# if pay['invoice_no']:
# key = pay['invoice_no']
# if key not in payments_map:
# payments_map[key] = []
# payments_map[key].append(pay)
# else:
# extra_payments.append(pay)
#
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "PMC Report"
#
# # Write Contractor Details
# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""])
# sheet.append(
# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ",
# "GST Type", contractor_info["GST_Registration_Type"]])
# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address",
# contractor_info["Address"]])
# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]])
# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]])
# sheet.append([])
#
# # Table Headers - include all hold types as separate columns
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
#
# hold_headers = [ht['hold_type'] for ht in hold_types]
#
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
#
# sheet.append(base_headers + hold_headers + payment_headers)
#
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set()
#
# # Process invoices
# for inv in invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get(invoice_no, [])
#
# # Process invoice row with first payment (if exists)
# if invoice_no not in seen_invoices:
# seen_invoices.add(invoice_no)
# first_payment = payments[0] if len(payments) > 0 else None
#
# # Base invoice data
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
#
# # Add hold amounts for each hold type
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
#
# # Add payment information
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
#
# sheet.append(row)
#
# if first_payment:
# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process GST release if exists (only if we have a matching GST record)
# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes:
# seen_gst_notes.add(inv["gst_invoice_no"])
#
# # Find the payment that matches this GST release
# gst_payment = None
# for payment in payments[1:]: # Skip first payment (already used for invoice)
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
#
# # If no payment found in the invoice's payments, check all payments
# if not gst_payment:
# gst_payments = payments_map.get(inv["gst_invoice_no"], [])
# if gst_payments:
# gst_payment = gst_payments[0]
#
# # GST release row (this will be in the same row, after the invoice information)
# gst_row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for GST release
# gst_row += ["" for _ in hold_headers]
#
# # Add GST payment information (same columns as invoice payment information)
# gst_row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
#
# sheet.append(gst_row)
#
# if gst_payment:
# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}"
# processed_payments.add(payment_id)
#
# # Process remaining payments as extra payments (if any)
# for payment in payments[1:]:
# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", payment['invoice_no'],
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for extra payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Process extra payments (null invoice_no)
# for payment in extra_payments:
# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [
# pmc_no, "", "", "", "", "",
# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount
# ]
#
# # Empty holds for null invoice payments
# row += ["" for _ in hold_headers]
#
# # Add payment information
# row += [
# "",
# payment["Payment_Amount"],
# payment["TDS_Payment_Amount"],
# payment["Total_amount"],
# payment["UTR"]
# ]
#
# sheet.append(row)
# processed_payments.add(payment_id)
#
# # Calculate totals
# total_basic_amount = 0
# total_tds_amount = 0
# total_sd_amount = 0
# total_on_commission = 0
# total_hold_amount = 0
# total_final_amount = 0
# total_payment_amount = 0
# total_tds_payment_amount = 0
# total_total_paid = 0
#
# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += float(row[6] or 0) # Basic_Amount
# total_tds_amount += float(row[11] or 0) # TDS_Amount
# total_sd_amount += float(row[12] or 0) # SD_Amount
# total_on_commission += float(row[13] or 0) # On_Commission
# total_final_amount += float(row[-5] or 0) # Final_Amount
# total_payment_amount += float(row[-4] or 0) # Payment_Amount
# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment
# total_total_paid += float(row[-2] or 0) # Total_Paid
#
# # Sum of hold amounts
# hold_start_col = len(base_headers)
# hold_end_col = hold_start_col + len(hold_headers)
# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col))
# except (ValueError, IndexError, TypeError):
# continue
#
# # Append totals row
# totals_row = [
# "TOTAL", "", "", "", "", "",
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", "", # Empty GST SD Amount
# ]
# if hold_headers:
# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1)
#
# # Add payment totals
# totals_row += [
# total_final_amount,
# total_payment_amount,
# total_tds_payment_amount,
# total_total_paid,
# "" # UTR column remains empty
# ]
#
# sheet.append([])
# sheet.append(totals_row)
# #new code added for small chart---summary
# total_hold_amount=Decimal('0.00')
# for d in invoices:
# total_hold_amount = total_hold_amount + d.get('SD_Amount', Decimal('0.00')) + d.get('On_Commission',
# Decimal(
# '0.00')) + d.get(
# 'Hydro_Testing', Decimal('0.00'))
# for data in hold_amounts:
# total_hold_amount = total_hold_amount + data.get('hold_amount', Decimal('0.00'))
# print("Total Hold Amount after adding the hold amount ", total_hold_amount)
#
# # Add payment information
# # Get today's date
# today_date = datetime.today().strftime('%A,%Y-%m-%d')
# # Add headers (optional)
# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]])
# sheet.append(["Date", today_date])
# sheet.append(["Description", "Amount"])
# # Add your values
# sheet.append(["Advance/Surplus", str(total_final_amount-total_payment_amount)])
# sheet.append(["Total Hold Amount", str(total_hold_amount)])
# sheet.append(["Amount With TDS", str(total_tds_payment_amount)])
# # new coded ended here for summary chart
# # Make totals row bold
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# # Save Excel file
# workbook.save(output_file)
# workbook.close()
#
# finally:
# cursor.close()
# connection.close()
#
# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True)
# @app.route('/download_pmc_report/<pmc_no>')
# def download_pmc_report(pmc_no):
# connection = config.get_db_connection()
# output_folder = "static/download"
# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx")
#
# if not os.path.exists(output_folder):
# os.makedirs(output_folder)
#
# cursor = connection.cursor(dictionary=True)
#
# try:
# cursor.callproc('GetContractorDetailsByPMC', [pmc_no])
# contractor_info = next(cursor.stored_results()).fetchone()
#
# if not contractor_info:
# return "No contractor found for this PMC No", 404
#
# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
# hold_types = next(cursor.stored_results()).fetchall()
# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
#
# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no])
# invoices = next(cursor.stored_results()).fetchall()
#
# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
# hold_amounts = next(cursor.stored_results()).fetchall()
# hold_data = {}
# for h in hold_amounts:
# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
#
# cursor.callproc('GetAllPaymentsByPMC', [pmc_no])
# all_payments = next(cursor.stored_results()).fetchall()
#
# payments_map = {}
# extra_payments = []
# for pay in all_payments:
# if pay['invoice_no']:
# payments_map.setdefault(pay['invoice_no'], []).append(pay)
# else:
# extra_payments.append(pay)
#
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "PMC Report"
#
# # Write contractor header
# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD."])
# sheet.append(["Contractor Name", contractor_info["Contractor_Name"], "", "GST No", contractor_info["GST_No"], "", "GST Type", contractor_info["GST_Registration_Type"]])
# sheet.append(["State", contractor_info["State_Name"], "", "PAN No", contractor_info["PAN_No"], "", "Address", contractor_info["Address"]])
# sheet.append(["District", contractor_info["District_Name"], "", "Mobile No", contractor_info["Mobile_No"]])
# sheet.append(["Block", contractor_info["Block_Name"], "", "Email", contractor_info["Email"]])
# sheet.append([])
#
# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
#
# hold_headers = [ht['hold_type'] for ht in hold_types]
# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
# sheet.append(base_headers + hold_headers + payment_headers)
# # Style the headers
# header_fill=PatternFill(start_color="ADD8E6",end_color="ADD8E6",fill_type="solid")
# header_font=Font(bold=True)
# for cell in sheet[sheet.max_row]:
# cell.font=header_font
# cell.fill=header_fill
#
# seen_invoices = set()
# seen_gst_notes = set()
# processed_payments = set()
#
# for inv in invoices:
# invoice_no = inv["Invoice_No"]
# payments = payments_map.get(invoice_no, [])
#
# if invoice_no not in seen_invoices:
# seen_invoices.add(invoice_no)
# first_payment = payments[0] if payments else None
#
# row = [
# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
# ]
#
# invoice_holds = hold_data.get(inv["Invoice_Id"], {})
# for ht_id in hold_type_map.keys():
# row.append(invoice_holds.get(ht_id, ""))
#
# row += [
# inv["Final_Amount"],
# first_payment["Payment_Amount"] if first_payment else "",
# first_payment["TDS_Payment_Amount"] if first_payment else "",
# first_payment["Total_amount"] if first_payment else "",
# first_payment["UTR"] if first_payment else ""
# ]
#
# sheet.append(row)
#
# if first_payment:
# processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}")
#
# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes:
# seen_gst_notes.add(inv["gst_invoice_no"])
# gst_payment = None
# for payment in payments[1:]:
# if payment['invoice_no'] == inv["gst_invoice_no"]:
# gst_payment = payment
# break
# if not gst_payment:
# gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0]
#
# gst_row = [
# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", ""
# ]
# gst_row += ["" for _ in hold_headers]
# gst_row += [
# inv["gst_final_amount"],
# gst_payment["Payment_Amount"] if gst_payment else "",
# gst_payment["TDS_Payment_Amount"] if gst_payment else "",
# gst_payment["Total_amount"] if gst_payment else "",
# gst_payment["UTR"] if gst_payment else ""
# ]
# sheet.append(gst_row)
# if gst_payment:
# processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}")
#
# for payment in payments[1:]:
# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
# if payment_id not in processed_payments:
# row = [pmc_no, "", "", "", "", payment['invoice_no']] + [""] * 10
# row += ["" for _ in hold_headers]
# row += [
# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"],
# payment["Total_amount"], payment["UTR"]
# ]
# sheet.append(row)
# processed_payments.add(payment_id)
#
# for payment in extra_payments:
# row = [pmc_no, "", "", "", "", ""] + [""] * 10
# row += ["" for _ in hold_headers]
# row += [
# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"],
# payment["Total_amount"], payment["UTR"]
# ]
# sheet.append(row)
#
# # Totals
# total_basic_amount = Decimal('0.00')
# total_tds_amount = Decimal('0.00')
# total_sd_amount = Decimal('0.00')
# total_on_commission = Decimal('0.00')
# total_final_amount = Decimal('0.00')
# total_payment_amount = Decimal('0.00')
# total_tds_payment_amount = Decimal('0.00')
# total_total_paid = Decimal('0.00')
# total_hold_amount_dynamic = Decimal('0.00')
#
# for row in sheet.iter_rows(min_row=8, max_row=sheet.max_row, values_only=True):
# try:
# total_basic_amount += Decimal(str(row[6] or 0))
# total_tds_amount += Decimal(str(row[11] or 0))
# total_sd_amount += Decimal(str(row[12] or 0))
# total_on_commission += Decimal(str(row[13] or 0))
# total_final_amount += Decimal(str(row[-5] or 0))
# total_payment_amount += Decimal(str(row[-4] or 0))
# total_tds_payment_amount += Decimal(str(row[-3] or 0))
# total_total_paid += Decimal(str(row[-2] or 0))
#
# for i in range(len(base_headers), len(base_headers) + len(hold_headers)):
# total_hold_amount_dynamic += Decimal(str(row[i] or 0))
# except:
# continue
#
# totals_row = [
# "TOTAL", "", "", "", "", "",
# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
# total_on_commission, "", ""
# ]
# totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1)
# totals_row += [
# total_final_amount,
# total_payment_amount,
# total_tds_payment_amount,
# total_total_paid,
# ""
# ]
#
# sheet.append([])
# sheet.append(totals_row)
#
# # Summary
# summary_hold = Decimal('0.00')
# for d in invoices:
# summary_hold += Decimal(str(d.get('SD_Amount', 0.00))) + Decimal(str(d.get('On_Commission', 0.00))) + Decimal(str(d.get('Hydro_Testing', 0.00)))
# for h in hold_amounts:
# summary_hold += Decimal(str(h.get('hold_amount', 0.00)))
#
# sheet.append([])
# today = datetime.today().strftime('%A, %Y-%m-%d')
# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]])
# sheet.append(["Date", today])
# sheet.append(["Description", "Amount"])
# sheet.append(["Advance/Surplus", str(total_final_amount - total_payment_amount)])
# sheet.append(["Total Hold Amount", str(summary_hold)])
# sheet.append(["Amount With TDS", str(total_payment_amount + total_tds_payment_amount)])
#
# for cell in sheet[sheet.max_row]:
# cell.font = Font(bold=True)
#
# workbook.save(output_file)
# workbook.close()
#
# finally:
# cursor.close()
# connection.close()
#
# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True)
@app.route('/download_pmc_report/<pmc_no>')
def download_pmc_report(pmc_no):
connection = config.get_db_connection()
output_folder = "static/download"
output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx")
if not os.path.exists(output_folder):
os.makedirs(output_folder)
cursor = connection.cursor(dictionary=True)
try:
cursor.callproc('GetContractorDetailsByPMC', [pmc_no])
contractor_info = next(cursor.stored_results()).fetchone()
if not contractor_info:
return "No contractor found for this PMC No", 404
cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]])
hold_types = next(cursor.stored_results()).fetchall()
hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types}
cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no])
invoices = next(cursor.stored_results()).fetchall()
# Credit Note
# Credit Note Fetch
cursor.execute("""
SELECT PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount,
GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No
FROM credit_note
WHERE Contractor_Id = %s
""", (pmc_no,))
credit_notes = cursor.fetchall()
# Build map by (PMC_No, Invoice_No)
credit_note_map = {}
for cn in credit_notes:
key = (cn["PMC_No"], cn["Invoice_No"]) # Use correct casing!
credit_note_map.setdefault(key, []).append(cn)
# Track already appended credit notes
appended_credit_keys = set()
cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]])
hold_amounts = next(cursor.stored_results()).fetchall()
hold_data = {}
for h in hold_amounts:
hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount']
cursor.callproc('GetAllPaymentsByPMC', [pmc_no])
all_payments = next(cursor.stored_results()).fetchall()
payments_map = {}
extra_payments = []
for pay in all_payments:
if pay['invoice_no']:
payments_map.setdefault(pay['invoice_no'], []).append(pay)
else:
extra_payments.append(pay)
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "PMC Report"
# Write contractor header
sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD."])
sheet.append(["Contractor Name", contractor_info["Contractor_Name"], "", "GST No", contractor_info["GST_No"], "", "GST Type", contractor_info["GST_Registration_Type"]])
sheet.append(["State", contractor_info["State_Name"], "", "PAN No", contractor_info["PAN_No"], "", "Address", contractor_info["Address"]])
sheet.append(["District", contractor_info["District_Name"], "", "Mobile No", contractor_info["Mobile_No"]])
sheet.append(["Block", contractor_info["Block_Name"], "", "Email", contractor_info["Email"]])
sheet.append([])
base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No",
"Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)",
"SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"]
hold_headers = [ht['hold_type'] for ht in hold_types]
payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"]
sheet.append(base_headers + hold_headers + payment_headers)
# Style the headers
header_fill=PatternFill(start_color="ADD8E6",end_color="ADD8E6",fill_type="solid")
header_font=Font(bold=True)
for cell in sheet[sheet.max_row]:
cell.font=header_font
cell.fill=header_fill
seen_invoices = set()
seen_gst_notes = set()
processed_payments = set()
for inv in invoices:
invoice_no = inv["Invoice_No"]
payments = payments_map.get(invoice_no, [])
if invoice_no not in seen_invoices:
seen_invoices.add(invoice_no)
first_payment = payments[0] if payments else None
row = [
pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"],
inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"],
inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"],
inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"]
]
invoice_holds = hold_data.get(inv["Invoice_Id"], {})
for ht_id in hold_type_map.keys():
row.append(invoice_holds.get(ht_id, ""))
row += [
inv["Final_Amount"],
first_payment["Payment_Amount"] if first_payment else "",
first_payment["TDS_Payment_Amount"] if first_payment else "",
first_payment["Total_amount"] if first_payment else "",
first_payment["UTR"] if first_payment else ""
]
sheet.append(row)
if first_payment:
processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}")
if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes:
seen_gst_notes.add(inv["gst_invoice_no"])
gst_payment = None
for payment in payments[1:]:
if payment['invoice_no'] == inv["gst_invoice_no"]:
gst_payment = payment
break
if not gst_payment:
gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0]
gst_row = [
pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"],
inv["gst_basic_amount"], "", "", "", "", "", "", "", "", ""
]
gst_row += ["" for _ in hold_headers]
gst_row += [
inv["gst_final_amount"],
gst_payment["Payment_Amount"] if gst_payment else "",
gst_payment["TDS_Payment_Amount"] if gst_payment else "",
gst_payment["Total_amount"] if gst_payment else "",
gst_payment["UTR"] if gst_payment else ""
]
sheet.append(gst_row)
if gst_payment:
processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}")
for payment in payments[1:]:
payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}"
if payment_id not in processed_payments:
row = [pmc_no, "", "", "", "", payment['invoice_no']] + [""] * 10
row += ["" for _ in hold_headers]
row += [
"", payment["Payment_Amount"], payment["TDS_Payment_Amount"],
payment["Total_amount"], payment["UTR"]
]
sheet.append(row)
processed_payments.add(payment_id)
for payment in extra_payments:
row = [pmc_no, "", "", "", "", ""] + [""] * 10
row += ["" for _ in hold_headers]
row += [
"", payment["Payment_Amount"], payment["TDS_Payment_Amount"],
payment["Total_amount"], payment["UTR"]
]
sheet.append(row)
# Credit Note row(s)
# Track already appended credit notes
appended_credit_keys = set()
# While writing invoices
key = (pmc_no, invoice_no)
if key in credit_note_map and key not in appended_credit_keys:
for cn in credit_note_map[key]:
credit_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", ""), "", "", "",
"", ""
]
credit_row += ["" for _ in hold_headers]
credit_row += [
cn.get("Final_Amount", ""),
cn.get("Total_Amount", ""),
cn.get("UTR", "")
]
sheet.append(credit_row)
appended_credit_keys.add(key)
# Totals
total_basic_amount = Decimal('0.00')
total_tds_amount = Decimal('0.00')
total_sd_amount = Decimal('0.00')
total_on_commission = Decimal('0.00')
total_final_amount = Decimal('0.00')
total_payment_amount = Decimal('0.00')
total_tds_payment_amount = Decimal('0.00')
total_total_paid = Decimal('0.00')
total_hold_amount_dynamic = Decimal('0.00')
for row in sheet.iter_rows(min_row=8, max_row=sheet.max_row, values_only=True):
try:
total_basic_amount += Decimal(str(row[6] or 0))
total_tds_amount += Decimal(str(row[11] or 0))
total_sd_amount += Decimal(str(row[12] or 0))
total_on_commission += Decimal(str(row[13] or 0))
total_final_amount += Decimal(str(row[-5] or 0))
total_payment_amount += Decimal(str(row[-4] or 0))
total_tds_payment_amount += Decimal(str(row[-3] or 0))
total_total_paid += Decimal(str(row[-2] or 0))
for i in range(len(base_headers), len(base_headers) + len(hold_headers)):
total_hold_amount_dynamic += Decimal(str(row[i] or 0))
except:
continue
totals_row = [
"TOTAL", "", "", "", "", "",
total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount,
total_on_commission, "", ""
]
if total_hold_amount_dynamic:
totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1)
totals_row += [
total_final_amount,
total_payment_amount,
total_tds_payment_amount,
total_total_paid,
""
]
sheet.append([])
sheet.append(totals_row)
# Summary
# summary_hold = Decimal('0.00')
# for d in invoices:
# summary_hold += Decimal(str(d.get('SD_Amount', 0))) + Decimal(str(d.get('On_Commission', 0))) + Decimal(str(d.get('Hydro_Testing', 0)))
# for h in hold_amounts:
# summary_hold += Decimal(str(h.get('hold_amount', 0)))
sheet.append([])
today = datetime.today().strftime('%A, %Y-%m-%d')
sheet.append(["Contractor Name", contractor_info["Contractor_Name"]])
sheet.append(["Date", today])
sheet.append(["Description", "Amount"])
sheet.append(["Advance/Surplus", str(total_final_amount - total_total_paid)])
sheet.append(["Total Hold Amount", str(total_hold_amount_dynamic)])
sheet.append(["Amount With TDS", str(total_tds_amount)])
for cell in sheet[sheet.max_row]:
cell.font = Font(bold=True)
workbook.save(output_file)
workbook.close()
finally:
cursor.close()
connection.close()
return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True)
# --------- Hold Types Controller --------------------------------------------
# Route to Add a New Hold Type
@app.route('/add_hold_type', methods=['POST', 'GET'])
def add_hold_type():
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True)
try:
# Fetch all hold types using the stored procedure
cursor.callproc("GetAllHoldTypes")
hold_types = []
for hold in cursor.stored_results():
hold_types = hold.fetchall()
if request.method == 'POST':
hold_type = request.form.get('hold_type', '').strip()
# Validation: Must start with a letter
if not hold_type or not hold_type[0].isalpha():
return jsonify({"status": "error", "message": "Hold Type must start with a letter."}), 400
# Validation: Check if it already exists (case-insensitive)
# cursor.execute("SELECT COUNT(*) AS count FROM hold_types WHERE LOWER(hold_type) = LOWER(%s)", (hold_type,))
# if cursor.fetchone()['count'] > 0:
# return jsonify({"status": "error", "message": "This Hold Type already exists."}), 400
# Call the procedure to check if the hold_type exists
cursor.callproc('CheckHoldTypeExists', [hold_type])
try:
# Insert new hold type into the database
# cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,))
# connection.commit()
cursor.callproc('SaveHoldType', [hold_type])
connection.commit()
return jsonify({"status": "success", "message": "Hold Type added successfully!"}), 201
except mysql.connector.Error as e:
connection.rollback()
return jsonify({"status": "error", "message": f"Database error: {str(e)}"}), 500
except mysql.connector.Error as e:
return jsonify({"status": "error", "message": f"Database error: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
return render_template('add_hold_type.html', Hold_Types_data=hold_types)
# Route to Update Hold Type
# @app.route('/update_hold_type/<int:id>', methods=['POST', 'GET'])
# def update_hold_type(id):
# # GET request: Show the form with the current hold type
# if request.method == 'GET':
# connection = config.get_db_connection()
# cursor = connection.cursor()
# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,))
# # hold_type = cursor.fetchone()
#
# cursor.callproc("GetHoldTypesById", (id,))
# for hold in cursor.stored_results():
# hold_type = hold.fetchone()
#
# cursor.close()
# connection.close()
#
# if not hold_type:
# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404
#
# return render_template('edit_hold_type.html', hold_type=hold_type)
#
# # POST request: Update the hold type
# if request.method == 'POST':
# new_hold_type = request.form.get('hold_type').strip()
#
# # Validation: Must start with a letter
# if not new_hold_type or not new_hold_type[0].isalpha():
# return jsonify(ResponseHandler.invalid_name('Hold Type')), 400
#
# connection = config.get_db_connection()
# cursor = connection.cursor()
#
# try:
# # Check if the hold type exists before updating
# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,))
# # hold_type = cursor.fetchone()
# cursor.callproc("GetHoldTypesById", (id,))
# for hold in cursor.stored_results():
# hold_type = hold.fetchone()
#
# if not hold_type:
# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404
#
# # Update the hold type
# # cursor.execute("UPDATE hold_types SET hold_type = %s WHERE hold_type_id = %s", (new_hold_type, id))
# cursor.callproc("UpdateHoldTypeById", (id,new_hold_type))
# connection.commit()
# return jsonify(ResponseHandler.update_success('Hold Type'))
#
# except mysql.connector.Error as e:
# connection.rollback()
# return jsonify(ResponseHandler.update_failure('Hold Type')), 500
# finally:
# cursor.close()
# connection.close()
@app.route('/update_hold_type/<int:id>', methods=['GET', 'POST'])
def update_hold_type(id):
connection = config.get_db_connection()
cursor = connection.cursor()
if request.method == 'GET':
cursor.callproc("GetHoldTypesById", (id,))
for hold in cursor.stored_results():
hold_type = hold.fetchone()
cursor.close()
connection.close()
if not hold_type:
flash('Hold Type not found.', 'error')
return redirect(url_for('add_hold_type'))
return render_template('edit_hold_type.html', hold_type=hold_type)
elif request.method == 'POST':
new_hold_type = request.form.get('hold_type', '').strip()
if not new_hold_type or not new_hold_type[0].isalpha():
flash('Invalid hold type name. Must start with a letter.', 'error')
return redirect(url_for('add_hold_type'))
try:
cursor.callproc("GetHoldTypesById", (id,))
for h in cursor.stored_results():
hold_type = h.fetchone()
if not hold_type:
flash('Hold Type not found.', 'error')
return redirect(url_for('add_hold_type'))
cursor.callproc("UpdateHoldTypeById", (id, new_hold_type))
connection.commit()
flash('Hold Type updated successfully!', 'success')
except mysql.connector.Error as e:
connection.rollback()
flash('Failed to update Hold Type.', 'error')
finally:
cursor.close()
connection.close()
return redirect(url_for('add_hold_type'))
# Route to Delete Hold Type
@app.route('/delete_hold_type/<int:id>', methods=['POST'])
def delete_hold_type(id):
connection = config.get_db_connection()
cursor = connection.cursor()
try:
# cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,))
# hold_type = cursor.fetchone()
cursor.callproc("GetHoldTypesById", (id,))
for hold in cursor.stored_results():
hold_type = hold.fetchone()
if not hold_type:
return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404
# Proceed with deletion
# cursor.execute("DELETE FROM hold_types WHERE hold_type_id = %s", (id,))
cursor.callproc("DeleteHoldType", (id,))
connection.commit()
return jsonify(ResponseHandler.delete_success('Hold Type'))
except mysql.connector.Error as e:
return jsonify(ResponseHandler.delete_failure('Hold Type')), 500
finally:
cursor.close()
connection.close()
# -- end hold types controlller --------------------
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)