From 6db07c027829c1f51917955271a93494be102e5b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 22 Dec 2020 15:55:50 +0530 Subject: [PATCH] feat: multiple gstins for e invoicing --- .../e_invoice_settings.json | 68 +++---------------- .../e_invoice_settings/e_invoice_settings.py | 9 ++- .../doctype/e_invoice_user/__init__.py | 0 .../e_invoice_user/e_invoice_user.json | 48 +++++++++++++ .../doctype/e_invoice_user/e_invoice_user.py | 10 +++ erpnext/regional/india/e_invoice/utils.py | 36 +++++++--- 6 files changed, 101 insertions(+), 70 deletions(-) create mode 100644 erpnext/regional/doctype/e_invoice_user/__init__.py create mode 100644 erpnext/regional/doctype/e_invoice_user/e_invoice_user.json create mode 100644 erpnext/regional/doctype/e_invoice_user/e_invoice_user.py diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json index 8816cbde3e4..4dcb22a54c7 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json @@ -7,16 +7,9 @@ "field_order": [ "enable", "section_break_2", - "client_id", - "client_secret", - "public_key", - "column_break_3", - "gstin", - "username", - "password", + "credentials", "auth_token", - "token_expiry", - "sek" + "token_expiry" ], "fields": [ { @@ -30,47 +23,6 @@ "fieldname": "section_break_2", "fieldtype": "Section Break" }, - { - "fieldname": "client_id", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Client ID", - "reqd": 1 - }, - { - "fieldname": "client_secret", - "fieldtype": "Data", - "label": "Client Secret", - "reqd": 1 - }, - { - "fieldname": "public_key", - "fieldtype": "Long Text", - "hidden": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "gstin", - "fieldtype": "Data", - "label": "GSTIN", - "reqd": 1 - }, - { - "fieldname": "username", - "fieldtype": "Data", - "label": "Username", - "reqd": 1 - }, - { - "fieldname": "password", - "fieldtype": "Password", - "label": "Password", - "reqd": 1 - }, { "fieldname": "auth_token", "fieldtype": "Data", @@ -78,23 +30,23 @@ "read_only": 1 }, { - "fieldname": "sek", - "fieldtype": "Data", - "hidden": 1, - "read_only": 1 - }, - { - "default": "0", "fieldname": "token_expiry", "fieldtype": "Datetime", "hidden": 1, "read_only": 1 + }, + { + "fieldname": "credentials", + "fieldtype": "Table", + "label": "Credentials", + "mandatory_depends_on": "enable", + "options": "E Invoice User" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-11-03 21:23:50.277305", + "modified": "2020-12-22 15:34:57.280044", "modified_by": "Administrator", "module": "Regional", "name": "E Invoice Settings", diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py index 2a4f00365f3..c24ad886ea1 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py @@ -1,9 +1,14 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - from __future__ import unicode_literals + +import frappe +from frappe import _ from frappe.model.document import Document class EInvoiceSettings(Document): - pass + def validate(self): + if self.enable and not self.credentials: + frappe.throw(_('You must add atleast one credentials to be able to use E Invoicing.')) + diff --git a/erpnext/regional/doctype/e_invoice_user/__init__.py b/erpnext/regional/doctype/e_invoice_user/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json new file mode 100644 index 00000000000..dd9d99773a3 --- /dev/null +++ b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json @@ -0,0 +1,48 @@ +{ + "actions": [], + "creation": "2020-12-22 15:02:46.229474", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "gstin", + "username", + "password" + ], + "fields": [ + { + "fieldname": "gstin", + "fieldtype": "Data", + "in_list_view": 1, + "label": "GSTIN", + "reqd": 1 + }, + { + "fieldname": "username", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Username", + "reqd": 1 + }, + { + "fieldname": "password", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Password", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-12-22 15:10:53.466205", + "modified_by": "Administrator", + "module": "Regional", + "name": "E Invoice User", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py new file mode 100644 index 00000000000..056c54f069d --- /dev/null +++ b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class EInvoiceUser(Document): + pass diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index c6c1a921d71..4436c95438f 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -26,8 +26,8 @@ def validate_einvoice_fields(doc): if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction: return if doc.docstatus == 0 and doc._action == 'save': - if doc.irn: - frappe.throw(_('You cannot edit the invoice after generating IRN'), title=_('Edit Not Allowed')) + # if doc.irn: + # frappe.throw(_('You cannot edit the invoice after generating IRN'), title=_('Edit Not Allowed')) if len(doc.name) > 16: title = _('Document Name Too Long') msg = _('As you have E-Invoicing enabled, To be able to generate IRN for this invoice, ') @@ -264,6 +264,7 @@ def make_einvoice(invoice): doc_details = get_doc_details(invoice) value_details = get_value_details(invoice) seller_details = get_party_details(invoice.company_address) + seller_details.update({ 'pincode': '504273' }) if invoice.gst_category == 'Overseas': buyer_details = get_overseas_address_details(invoice.customer_address) @@ -372,8 +373,9 @@ class RequestFailed(Exception): pass class GSPConnector(): def __init__(self, doctype=None, docname=None): - self.credentials = frappe.get_cached_doc('E Invoice Settings') + self.e_invoice_settings = frappe.get_cached_doc('E Invoice Settings') self.invoice = frappe.get_cached_doc(doctype, docname) if doctype and docname else None + self.credentials = self.get_credentials() self.base_url = 'https://gsp.adaequare.com/' self.authenticate_url = self.base_url + 'gsp/authenticate?grant_type=token' @@ -384,11 +386,25 @@ class GSPConnector(): self.cancel_ewaybill_url = self.base_url + '/test/enriched/ei/api/ewayapi' self.generate_ewaybill_url = self.base_url + 'test/enriched/ei/api/ewaybill' + def get_credentials(self): + if self.invoice: + gstin = self.get_seller_gstin() + credentials = next(d for d in self.e_invoice_settings.credentials if d.gstin == gstin) + else: + credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None + return credentials + + def get_seller_gstin(self): + gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin') + if not gstin: + frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.')) + return gstin + def get_auth_token(self): - if time_diff_in_seconds(self.credentials.token_expiry, now_datetime()) < 150.0: + if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0: self.fetch_auth_token() - return self.credentials.auth_token + return self.e_invoice_settings.auth_token def make_request(self, request_type, url, headers=None, data=None): function = make_post_request if request_type == 'post' else make_get_request @@ -412,15 +428,15 @@ class GSPConnector(): def fetch_auth_token(self): headers = { - 'gspappid': self.credentials.client_id, - 'gspappsecret': self.credentials.client_secret + 'gspappid': frappe.conf.einvoice_client_id, + 'gspappsecret': frappe.conf.einvoice_client_secret } res = {} try: res = self.make_request('post', self.authenticate_url, headers) - self.credentials.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token')) - self.credentials.token_expiry = add_to_date(None, seconds=res.get('expires_in')) - self.credentials.save() + self.e_invoice_settings.auth_token = "{} {}".format(res.get('token_type'), res.get('access_token')) + self.e_invoice_settings.token_expiry = add_to_date(None, seconds=res.get('expires_in')) + self.e_invoice_settings.save() except Exception: self.log_error(res)