final payment reconciliation

This commit is contained in:
2026-04-06 10:46:47 +05:30
parent 0ca1749757
commit 5ae1fbe320
24 changed files with 766 additions and 638 deletions

View File

@@ -1,14 +1,4 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper
import os
import config
import re
import mysql.connector
from mysql.connector import Error
from model.ItemCRUD import ItemCRUD, itemCRUDMapping

View File

@@ -1,6 +1,5 @@
from mysql.connector import Error
import config
from datetime import datetime
class ContractorInfo:
@@ -24,39 +23,39 @@ class ContractorInfo:
if connection.is_connected():
connection.close()
# def fetchalldata(self):
# """Fetch hold types and invoices for contractor."""
# data = {}
# try:
# connection = config.get_db_connection()
# with connection.cursor(dictionary=True, buffered=True) as cursor:
# # Fetch Hold Types
# cursor.callproc('GetHoldAmountsAndHoldTypeByCtr', [self.ID])
# hold_types = []
# 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}
# data['hold_types'] = hold_type_map
def fetchalldata(self):
"""Fetch hold types and invoices for contractor."""
data = {}
try:
connection = config.get_db_connection()
with connection.cursor(dictionary=True, buffered=True) as cursor:
# Fetch Hold Types
cursor.callproc('GetHoldAmountsAndHoldTypeByCtr', [self.ID])
hold_types = []
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}
data['hold_types'] = hold_type_map
# # Fetch Invoices
# cursor.callproc('GetInvoicesByContractor', [self.ID])
# invoices = []
# for result in cursor.stored_results():
# invoices = result.fetchall()
# Fetch Invoices
cursor.callproc('GetInvoicesByContractor', [self.ID])
invoices = []
for result in cursor.stored_results():
invoices = result.fetchall()
# # Remove duplicate invoices
# seen_ids = set()
# unique_invoices = []
# for inv in invoices:
# if inv['Invoice_Id'] not in seen_ids:
# seen_ids.add(inv['Invoice_Id'])
# unique_invoices.append(inv)
# data['invoices'] = unique_invoices
# Remove duplicate invoices
seen_ids = set()
unique_invoices = []
for inv in invoices:
if inv['Invoice_Id'] not in seen_ids:
seen_ids.add(inv['Invoice_Id'])
unique_invoices.append(inv)
data['invoices'] = unique_invoices
# except Error as e:
# print(f"Error fetching contractor data: {e}")
# finally:
# if connection.is_connected():
# connection.close()
except Error as e:
print(f"Error fetching contractor data: {e}")
finally:
if connection.is_connected():
connection.close()
# return data
return data

View File

@@ -33,7 +33,7 @@ class HoldTypes:
self.isSuccess = hold.isSuccess
self.resultMessage = hold.resultMessage
# Convert tuple → dictionary
# Convert tuple → dictionary
data = []
for row in rows:
data.append({
@@ -51,7 +51,7 @@ class HoldTypes:
self.isSuccess = hold.isSuccess
self.resultMessage = hold.resultMessage
#Convert tuple → dictionary
#Convert tuple → dictionary
if row:
return {
"hold_type_id": row[0],

View File

@@ -1,11 +1,4 @@
import config
from datetime import datetime
from flask import send_file
import openpyxl
from openpyxl.styles import Font, PatternFill
from model.excel import excel
from model.FolderAndFile import FolderAndFile
class ReportHelper:
isSuccess = False

View File

@@ -6,18 +6,18 @@ from decimal import Decimal
class UnifiedReportService:
# Static variable to cache report data
_cached_report_data = None
_cached_contractor_id = None
_cached_pmc_no = None
# _cached_report_data = None
# _cached_contractor_id = None
# _cached_pmc_no = None
@staticmethod
def get_report(contractor_id=None, pmc_no=None):
# If cached and same request, return cached data
if (UnifiedReportService._cached_report_data and
contractor_id == UnifiedReportService._cached_contractor_id and
pmc_no == UnifiedReportService._cached_pmc_no):
return UnifiedReportService._cached_report_data
# if (UnifiedReportService._cached_report_data and
# contractor_id == UnifiedReportService._cached_contractor_id and
# pmc_no == UnifiedReportService._cached_pmc_no):
# return UnifiedReportService._cached_report_data
connection = config.get_db_connection()
cursor = connection.cursor(dictionary=True, buffered=True)
@@ -42,25 +42,16 @@ class UnifiedReportService:
# ================= INVOICES =================
if pmc_no:
invoices = ReportHelper.execute_sp(cursor,'GetInvoicesByContractorOrPMCNo',[None, pmc_no]) or []
credit_notes = ReportHelper.execute_sp(cursor,'NewGetCreditNotesByPMCNo',[pmc_no]) or []
gst_rel = ReportHelper.execute_sp(cursor,'GetGSTReleaseDetailsByPMC',[pmc_no]) or []
payments = ReportHelper.execute_sp(cursor,'GetPaymentsByPMC',[pmc_no]) or []
else:
invoices = ReportHelper.execute_sp(cursor,'GetInvoicesByContractorOrPMCNo',[contractor_id, None]) or []
credit_notes = ReportHelper.execute_sp(cursor,'GetCreditNotesByContractor',[contractor_id]) or []
gst_rel = ReportHelper.execute_sp(cursor,'GstReleasesByContractorId',[contractor_id]) or []
payments = ReportHelper.execute_sp(cursor,'GetPayments',[contractor_id]) or []
print(payments)
# ================= HOLD AMOUNTS =================
hold_amounts = ReportHelper.execute_sp(cursor,'GetHoldAmountsByContractor',[contractor_id]) or []
@@ -99,9 +90,11 @@ class UnifiedReportService:
"sum_gst_final_amt": sum(r.get("Final_Amount", 0) or 0 for r in gst_rel),
"sum_gst_total_amt": sum(r.get("Total_Amount", 0) or 0 for r in gst_rel),
"sum_pay_total_amt": sum(Decimal(r.get("Total_amount") or 0) for r in payments),
"sum_pay_payment_amt": sum(Decimal(r.get("Payment_Amount") or 0) for r in payments), # same as above if you want
"sum_pay_payment_amt": sum(Decimal(r.get("Payment_Amount") or 0) for r in payments),
"sum_pay_tds_payment_amt": sum(Decimal(r.get("TDS_Payment_Amount") or 0) for r in payments),
"sum_credit_basic_amt": sum(Decimal(c.get("Basic_Amount") or 0) for c in credit_notes),
"sum_credit_final_amt": sum(Decimal(c.get("Final_Amount") or 0) for c in credit_notes),
"sum_credit_total_amt": sum(Decimal(c.get("Total_Amount") or 0) for c in credit_notes),
}
# Prepare final report
@@ -119,10 +112,10 @@ class UnifiedReportService:
"total": total
}
# Cache the report
UnifiedReportService._cached_report_data = report_data
UnifiedReportService._cached_contractor_id = contractor_id
UnifiedReportService._cached_pmc_no = pmc_no
# # Cache the report
# UnifiedReportService._cached_report_data = report_data
# UnifiedReportService._cached_contractor_id = contractor_id
# UnifiedReportService._cached_pmc_no = pmc_no
return report_data
@@ -155,6 +148,7 @@ class UnifiedReportService:
report_data["credit_note_map"],
report_data["gst_release_map"],
report_data["payments"],
report_data["total"],
output_file
)

View File

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

View File

@@ -1,28 +1,13 @@
import openpyxl
from openpyxl.styles import Font, PatternFill
import config
from flask_login import current_user
from model.Log import LogHelper
# from model.Report import ReportHelper
from model.FolderAndFile import FolderAndFile
class excel:
@staticmethod
# def generate_excel(contractor_id, contInfo, invoices, hold_types, hold_data,
# credit_note_map, gst_release_map, output_file):
def generate_excel(
contractor_id,
info,
invoices,
hold_types,
hold_data,
credit_note_map,
gst_release_map,
payments,
output_file
):
def generate_excel(contractor_id,info, invoices, hold_types, hold_data, credit_note_map, gst_release_map, payments,total,output_file):
workbook = openpyxl.Workbook()
sheet = workbook.active
@@ -62,7 +47,6 @@ class excel:
else ""
)
# key = (pmc_no, invoice_no)
key = (pmc_no)
# Yellow separator
@@ -161,5 +145,43 @@ class excel:
appended_credit_keys.add(key)
# total calculation
hold_totals = {ht['hold_type_id']: 0 for ht in hold_types}
for inv in invoices:
invoice_holds = hold_data.get(inv["Invoice_Id"], {})
for ht_id in hold_totals:
hold_totals[ht_id] += invoice_holds.get(ht_id, 0) or 0
totalrow = ["Total","-","-","-","-","-",total.get('sum_invo_basic_amt', 0)+total.get('sum_gst_basic_amt')+ total.get('sum_credit_basic_amt'),
total.get('sum_invo_debit_amt', 0), total.get('sum_invo_after_debit_amt', 0),total.get('sum_invo_gst_amt', 0),total.get('sum_invo_amt', 0),
total.get('sum_invo_tds_amt', 0),total.get('sum_invo_ds_amt', 0),total.get('sum_invo_on_commission', 0), total.get('sum_invo_hydro_test', 0),
total.get('sum_invo_gst_sd_amt', 0)]
for ht in hold_types:
totalrow.append(hold_totals.get(ht['hold_type_id'], 0))
totalrow += [ total.get('sum_invo_final_amt', 0)+ total.get('sum_gst_final_amt', 0) + total.get('sum_credit_final_amt', 0),"","",
total.get('sum_pay_total_amt', 0)+ total.get('sum_gst_total_amt', 0) + total.get('sum_credit_total_amt', 0)]
sheet.append(totalrow)
# Apply style (color + bold)
for cell in sheet[sheet.max_row]:
cell.fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") # Yellow
cell.font = Font(bold=True)
# summary report
sheet.append([""])
sheet.append([""])
sheet.append(["","","Summary Report","",""])
summary1 = ["", "Advance / Suplus" , total.get('sum_pay_total_amt', 0)+ total.get('sum_gst_total_amt', 0) + total.get('sum_credit_total_amt', 0)]
summary2 = ["", "Hold Amt", total["sum_invo_hold_amt"]]
summary3 = ["", "Amount With TDS", total["sum_invo_tds_amt"]]
sheet.append(summary1)
sheet.append(summary2)
sheet.append(summary3)
# SAVE ONCE AT END
workbook.save(output_file)

View File

@@ -14,12 +14,6 @@ class GSTRelease:
try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Print the full form data
print("===== DEBUG: FORM DATA =====")
for key, value in request.form.items():
print(f"{key} : {value}")
print("=============================")
data = {
"PMC_No": request.form.get("PMC_No", "").strip(),
"Invoice_No": request.form.get("Invoice_No", "").strip(),
@@ -30,10 +24,6 @@ class GSTRelease:
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
}
print("===== DEBUG: PARSED DATA =====")
print(data)
print("==============================")
# Add GST Release
gst.AddItem(
request=request,
@@ -42,8 +32,6 @@ class GSTRelease:
storedprocadd="AddGSTReleaseFromExcel"
)
print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
self.isSuccess = gst.isSuccess
self.resultMessage = str(gst.resultMessage)
@@ -69,10 +57,6 @@ class GSTRelease:
"p_gst_release_id": gst_release_id
}
print("===== DEBUG: UPDATE DATA =====")
print(data)
print("==============================")
# Call your stored procedure
gst.EditItem(
request=request,