feat: add document verification API views and intelligent name matching utilities
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 44s

This commit is contained in:
vickytechkey 2026-10-16 12:53:23 +05:30
parent 9e60dc2f56
commit 14ca86323e

View file

@ -284,30 +284,112 @@ class SellerPanGstVerifyView(APIView):
(gst_records[0].get("document_id") or gst_records[0].get("gstin")) if gst_records and isinstance(gst_records[0], dict) else None
)
target_gstin = entered_gstin or primary_gstin or ""
gstin_matched = False
if entered_gstin:
gstin_matched = any(
(g.get("document_id") or g.get("gstin") or "").upper() == entered_gstin
for g in gst_records if isinstance(g, dict)
)
gst_details = {}
verified_gst_name = ""
gst_name_match = {"matched": True, "score": 100.0, "reason": "No GST provided"}
gst_error = None
if target_gstin:
# Check if entered GSTIN exists in fetched GST records for this PAN
matching_gst_record = None
for g in gst_records:
if isinstance(g, dict):
doc_id = (g.get("document_id") or g.get("gstin") or "").upper()
if doc_id == target_gstin:
matching_gst_record = g
gstin_matched = True
break
# Validate that 15-character GSTIN contains the 10-character PAN at chars 3-12
if len(target_gstin) == 15 and target_gstin[2:12].upper() != pan.upper():
gstin_matched = False
gst_error = f"GSTIN '{target_gstin}' does not match the provided PAN card '{pan}'."
# Check if matching record has legal_name
if matching_gst_record and matching_gst_record.get("legal_name"):
verified_gst_name = matching_gst_record.get("legal_name")
gst_details = matching_gst_record
else:
# Call gst_verify to get legal_name and active status
try:
gst_verify_result = engine.verify(
document_type="gst_verify",
data={"gst_number": target_gstin},
preferred_vendor=preferred_vendor,
)
for att in gst_verify_result.attempts:
att["attempt_number"] = len(all_attempts) + 1
all_attempts.append(att)
if gst_verify_result.is_success and gst_verify_result.data:
gst_details = gst_verify_result.data.get("gst_details") or gst_verify_result.data
verified_gst_name = (
gst_verify_result.data.get("legal_name")
or gst_details.get("legalName")
or gst_details.get("tradeName")
or ""
)
gstin_matched = True
except Exception as gst_err:
logger.warning("Optional gst_verify call failed: %s", gst_err)
# If verified_gst_name wasn't returned by upstream, fallback to matching record or verified_pan_name if PAN matches GSTIN
if not verified_gst_name and gstin_matched:
verified_gst_name = verified_pan_name
# Compare GST legal name against seller full name and verified PAN name
if verified_gst_name:
gst_name_match = compare_names(seller_name, verified_gst_name)
if not gst_name_match["matched"] and verified_pan_name:
pan_to_gst_match = compare_names(verified_pan_name, verified_gst_name)
if pan_to_gst_match["matched"]:
gst_name_match = pan_to_gst_match
total_latency = (time.time() - start_time) * 1000
overall_success = pan_result.is_success and match_result["matched"]
overall_status = "SUCCESS" if overall_success else "NAME_MISMATCH"
pan_name_matched = bool(match_result.get("matched", False))
gst_name_matched = bool(gst_name_match.get("matched", False)) if target_gstin else True
if gst_error:
overall_success = False
overall_status = "GSTIN_PAN_MISMATCH"
error_msg = gst_error
elif not pan_name_matched:
overall_success = False
overall_status = "NAME_MISMATCH"
error_msg = f"PAN name mismatch: The entered name '{seller_name}' does not match PAN record '{verified_pan_name}' ({match_result.get('reason', '')})"
elif target_gstin and not gst_name_matched:
overall_success = False
overall_status = "GST_NAME_MISMATCH"
error_msg = f"GST name mismatch: The entered name '{seller_name}' does not match GST legal name '{verified_gst_name}' ({gst_name_match.get('reason', '')})"
else:
overall_success = pan_result.is_success and pan_name_matched
overall_status = "SUCCESS" if overall_success else "FAILED"
error_msg = None
combined_data = {
"pan_number": pan,
"verified_pan_name": verified_pan_name,
"seller_name": seller_name,
"name_matched": match_result["matched"],
"match_score": match_result["score"],
"match_reason": match_result["reason"],
"name_matched": pan_name_matched,
"pan_name_matched": pan_name_matched,
"pan_match_score": match_result.get("score", 0.0),
"pan_match_reason": match_result.get("reason", ""),
"pan_details": pan_data,
"gst_number": target_gstin,
"verified_gst_name": verified_gst_name,
"gst_name_matched": gst_name_matched,
"gst_match_score": gst_name_match.get("score", 0.0),
"gst_match_reason": gst_name_match.get("reason", ""),
"gst_details": gst_details,
"gst_records": gst_records,
"active_gstins": active_gstins,
"primary_gstin": primary_gstin,
"entered_gstin": entered_gstin,
"gstin_matched": gstin_matched,
"is_seller_verified": overall_success,
}
# Step 4: Persist verification transaction & attempt logs in database
@ -335,16 +417,13 @@ class SellerPanGstVerifyView(APIView):
latency_ms=att.get("latency_ms", 0.0),
)
error_msg = None
if not overall_success:
error_msg = f"Name mismatch: The entered name '{seller_name}' does not match PAN record '{verified_pan_name}' ({match_result['reason']})"
res_payload = {
"reference_id": str(tx.reference_id),
"is_success": overall_success,
"status": overall_status,
"document_type": "SELLER_PAN_GST",
"successful_vendor": pan_result.successful_vendor,
"is_seller_verified": overall_success,
"data": combined_data,
"attempts_count": len(all_attempts),
"attempts": all_attempts,