Skip to content

Commit

Permalink
fix: new pan doctype
Browse files Browse the repository at this point in the history
(cherry picked from commit e965219)
  • Loading branch information
Sanket322 authored and mergify[bot] committed Aug 22, 2024
1 parent 78eff3a commit 64a1ff8
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 23 deletions.
7 changes: 6 additions & 1 deletion india_compliance/gst_india/client_scripts/party.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,15 @@ function set_gstin_options_and_status(doctype) {
refresh(frm) {
set_gstin_options(frm);
india_compliance.set_gstin_status(frm.get_field("gstin"));
india_compliance.set_pan_status(frm.get_field("pan"));
if (doctype !== "Address") {
india_compliance.set_pan_status(frm.get_field("pan"));
}
},
gstin(frm) {
india_compliance.set_gstin_status(frm.get_field("gstin"));
if (doctype !== "Address") {
india_compliance.set_pan_status(frm.get_field("pan"));
}
},
pan(frm) {
india_compliance.set_pan_status(frm.get_field("pan"));
Expand Down
Empty file.
13 changes: 13 additions & 0 deletions india_compliance/gst_india/doctype/pan/pan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024, Resilient Tech and contributors
// For license information, please see license.txt

frappe.ui.form.on("Pan", {
refresh(frm) {
frm.add_custom_button(__("Refresh PAN Status"), () => {
frm.call("update_pan_status");
});
},
pan(frm) {
frm.doc.pan = frm.doc.pan.toUpperCase();
},
});
52 changes: 52 additions & 0 deletions india_compliance/gst_india/doctype/pan/pan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2024-07-31 13:10:35.277697",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"pan",
"pan_status"
],
"fields": [
{
"fieldname": "pan",
"fieldtype": "Data",
"label": "Pan"
},
{
"fieldname": "pan_status",
"fieldtype": "Data",
"label": "Pan Status"
},
{
"fieldname": "last_updated_on",
"fieldtype": "Datetime",
"label": "Last Updated On"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-07-31 13:11:07.830805",
"modified_by": "Administrator",
"module": "GST India",
"name": "Pan",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
14 changes: 14 additions & 0 deletions india_compliance/gst_india/doctype/pan/pan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2024, Resilient Tech and contributors
# For license information, please see license.txt

import frappe
from frappe.model.document import Document

from india_compliance.gst_india.overrides.party import get_pancard_status


class Pan(Document):
@frappe.whitelist()
def update_pan_status(self):
get_pancard_status(self.pan, True)
frappe.msgprint("PAN Status Updated")
9 changes: 9 additions & 0 deletions india_compliance/gst_india/doctype/pan/test_pan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024, Resilient Tech and Contributors
# See license.txt

# import frappe
from frappe.tests.utils import FrappeTestCase


class TestPan(FrappeTestCase):
pass
115 changes: 104 additions & 11 deletions india_compliance/gst_india/overrides/party.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import json
import random

import requests

import frappe
from frappe import _
from frappe.contacts.doctype.address.address import get_address_display
from frappe.utils import now

from india_compliance.gst_india.utils import (
guess_gst_category,
Expand Down Expand Up @@ -168,26 +170,117 @@ def make_address(doc):
).insert()


@frappe.whitelist()
def validate_pancard_status(pan):
multiplication_table = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
]

permutation_table = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
[8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
]

inverse_table = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]


def verhoeff_checksum(number: str) -> int:
"""Calculate the Verhoeff checksum digit."""
c = 0
n = len(number)
for i in range(n):
c = multiplication_table[c][
permutation_table[(i + 1) % 8][int(number[n - i - 1])]
]
return inverse_table[c]


def generate_aadhaar_number():
"""Generate a valid Aadhaar number using the Verhoeff algorithm."""
base_number = "".join(str(random.randint(0, 9)) for _ in range(11))
check_digit = verhoeff_checksum(base_number)
return base_number + str(check_digit)


def fetch_pan_status_from_api(aadhaar_number, pan, force_update):
"""This is an unofficial API"""
url = "https://eportal.incometax.gov.in/iec/servicesapi/getEntity"
payload = {
"aadhaarNumber": "887598175965", # this is random generated aadhaar_number
"aadhaarNumber": aadhaar_number, # this is random generated aadhaar_number
"pan": pan,
"preLoginFlag": "Y",
"serviceName": "linkAadhaarPreLoginService",
}
response = requests.post(url, json=payload)
messages = response.json().get("messages")
if not messages:

try:
response = requests.post(url, json=payload)
return response.json().get("messages", [])
except ConnectionError:
msg = "Connection error. Please retry after some time."
except Exception:
msg = "An error occurred. Please retry after some time."

if force_update:
frappe.throw(msg)
else:
return ""


def update_pan_document(pan, status):
if docname := frappe.db.exists("Pan", pan):
doc = frappe.get_doc("Pan", docname)
else:
doc = frappe.new_doc("Pan")

doc.update(
{
"pan": pan,
"pan_status": status,
"last_updated_on": now(),
}
)
doc.save()


def get_pancard_status(pan, force_update):
aadhaar_number = generate_aadhaar_number()
messages = fetch_pan_status_from_api(aadhaar_number, pan, force_update)

if not messages:
return

error_code_desc_map = {
"EF40124": "Linked", # pan linked to given aadhar card
"EF40026": "Linked", # pan linked but not to given aadhar card
"EF40089": "Invalid PAN",
"EF40119": "Not an Individual Taxpayer",
"EF40124": "Valid", # pan linked to generated aadhar card number
"EF40026": "Valid", # pan linked but not to generated aadhar card
"EF40119": "Valid", # not an individual taxpayer : AAACS8577K
"EF40089": "Invalid", # invalid pan : OIMPS2320M
"EF40024": "Not-Linked",
"EF40077": "", # Invalid Aadhaar number
}

return error_code_desc_map.get(messages[0].get("code", ""), "")
status = error_code_desc_map.get(messages[0].get("code", ""), "")
if not status:
return

update_pan_document(pan, status)


@frappe.whitelist()
def get_pan_status(pan, force_update=False):
if force_update or not frappe.db.exists("Pan", pan):
get_pancard_status(pan, force_update)

return frappe.db.get_value("Pan", pan, ["pan_status", "last_updated_on"])
48 changes: 37 additions & 11 deletions india_compliance/public/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,50 @@ Object.assign(india_compliance, {
return message;
},

async set_pan_status(field){
async set_pan_status(field, force_update = null) {
const pan = field.value;
if (!pan || pan.length !== 10) return field.set_description("");

const { message } = await frappe.call({
method: "india_compliance.gst_india.overrides.party.validate_pancard_status",
args: { pan },
method: "india_compliance.gst_india.overrides.party.get_pan_status",
args: { pan, force_update },
});
if (!message) return;

if (!message || message == "") return field.set_description("");

const STATUS_COLORS = { 'Linked': "green", 'Not-Linked': "red", "Not an Individual Taxpayer" : "green",
"Invalid PAN" : "red" };
pan_status = `<div class="d-flex indicator ${STATUS_COLORS[message] || "orange"}">
Status:&nbsp;<strong>${message}</strong>
</div>`;
const [pan_status, datetime] = message;
const STATUS_COLORS = {
Valid: "green",
"Not-Linked": "red",
Invalid: "red",
};
let pan_desc;

if (!pan_status) {
const user_date = frappe.datetime.str_to_user(datetime);
const pretty_date = frappe.datetime.prettyDate(datetime);

pan_desc = $(`<div class="text-right">
<span class="pan-last-updated">
<span title="${user_date}">
${datetime ? "updated " + pretty_date : ""}
</span>
<svg class="icon icon-sm refresh-pan" style="cursor: pointer;">
<use href="#icon-refresh"></use>
</svg>
</span>
</div>`);

field.set_description(pan_status);
pan_desc.find(".refresh-pan").on("click", async function () {
await india_compliance.set_pan_status(field, true);
});
} else {
pan_desc = `<div class="d-flex indicator ${
STATUS_COLORS[pan_status] || "orange"
}">
Status:&nbsp;<strong>${pan_status}</strong>
</div>`;
}
return field.set_description(pan_desc);
},

validate_gst_transporter_id(transporter_id) {
Expand Down

0 comments on commit 64a1ff8

Please sign in to comment.