feat: add document verification API views and intelligent name matching utilities
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 44s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 44s
This commit is contained in:
parent
9e60dc2f56
commit
14ca86323e
1 changed files with 93 additions and 14 deletions
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue