mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-12 06:48:31 +00:00
feat: update timeline on einvoice actions
This commit is contained in:
@@ -32,4 +32,5 @@ def execute():
|
||||
add_print_formats()
|
||||
|
||||
frappe.db.set_value('Custom Field', { 'fieldname': 'mode_of_transport' }, 'default', '')
|
||||
frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'depends_on', 'eval:doc.mode_of_transport == "Road"')
|
||||
frappe.db.set_value('Custom Field', { 'fieldname': 'vehicle_no' }, 'depends_on', 'eval:doc.mode_of_transport == "Road"')
|
||||
frappe.db.set_value('Custom Field', { 'fieldname': 'ewaybill' }, 'depends_on', 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)')
|
||||
@@ -7,7 +7,13 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
|
||||
if (!einvoicing_enabled || !valid_supply_type) return;
|
||||
|
||||
const { docstatus, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc;
|
||||
const { doctype, docstatus, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc;
|
||||
|
||||
const add_custom_button = (label, action) => {
|
||||
if (!frm.custom_buttons[label]) {
|
||||
frm.add_custom_button(label, action, __('E Invoicing'));
|
||||
}
|
||||
}
|
||||
|
||||
if (ewaybill && irn) {
|
||||
frm.set_df_property('ewaybill', 'read_only', 1);
|
||||
@@ -17,13 +23,13 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
const action = () => {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.generate_irn',
|
||||
args: { docname: name },
|
||||
args: { doctype, docname: name },
|
||||
freeze: true,
|
||||
callback: () => frm.reload_doc()
|
||||
})
|
||||
};
|
||||
|
||||
frm.add_custom_button(__("Generate IRN"), action, __('E Invoicing'));
|
||||
add_custom_button(__("Generate IRN"), action);
|
||||
}
|
||||
|
||||
if (docstatus == 1 && irn && !irn_cancelled && !ewaybill) {
|
||||
@@ -52,6 +58,7 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
|
||||
args: {
|
||||
doctype,
|
||||
docname: name,
|
||||
irn: irn,
|
||||
reason: data.reason.split('-')[0],
|
||||
@@ -66,105 +73,23 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
});
|
||||
d.show();
|
||||
};
|
||||
frm.add_custom_button(__("Cancel IRN"), action, __("E Invoicing"));
|
||||
add_custom_button(__("Cancel IRN"), action);
|
||||
}
|
||||
|
||||
if (irn && !irn_cancelled && !ewaybill) {
|
||||
const fields = [
|
||||
{
|
||||
'fieldname': 'transporter',
|
||||
'label': 'Transporter',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Supplier',
|
||||
'default': frm.doc.transporter
|
||||
},
|
||||
{
|
||||
'fieldname': 'gst_transporter_id',
|
||||
'label': 'GST Transporter ID',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'transporter.gst_transporter_id',
|
||||
'default': frm.doc.gst_transporter_id
|
||||
},
|
||||
{
|
||||
'fieldname': 'driver',
|
||||
'label': 'Driver',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Driver',
|
||||
'default': frm.doc.driver
|
||||
},
|
||||
{
|
||||
'fieldname': 'lr_no',
|
||||
'label': 'Transport Receipt No',
|
||||
'fieldtype': 'Data',
|
||||
'default': frm.doc.lr_no
|
||||
},
|
||||
{
|
||||
'fieldname': 'vehicle_no',
|
||||
'label': 'Vehicle No',
|
||||
'fieldtype': 'Data',
|
||||
'depends_on': 'eval:(doc.mode_of_transport === "Road")',
|
||||
'default': frm.doc.vehicle_no
|
||||
},
|
||||
{
|
||||
'fieldname': 'distance',
|
||||
'label': 'Distance (in km)',
|
||||
'fieldtype': 'Float',
|
||||
'default': frm.doc.distance
|
||||
},
|
||||
{
|
||||
'fieldname': 'transporter_col_break',
|
||||
'fieldtype': 'Column Break',
|
||||
},
|
||||
{
|
||||
'fieldname': 'transporter_name',
|
||||
'label': 'Transporter Name',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'transporter.name',
|
||||
'read_only': 1,
|
||||
'default': frm.doc.transporter_name
|
||||
},
|
||||
{
|
||||
'fieldname': 'mode_of_transport',
|
||||
'label': 'Mode of Transport',
|
||||
'fieldtype': 'Select',
|
||||
'options': `\nRoad\nAir\nRail\nShip`,
|
||||
'default': frm.doc.mode_of_transport
|
||||
},
|
||||
{
|
||||
'fieldname': 'driver_name',
|
||||
'label': 'Driver Name',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'driver.full_name',
|
||||
'read_only': 1,
|
||||
'default': frm.doc.driver_name
|
||||
},
|
||||
{
|
||||
'fieldname': 'lr_date',
|
||||
'label': 'Transport Receipt Date',
|
||||
'fieldtype': 'Date',
|
||||
'default': frm.doc.lr_date
|
||||
},
|
||||
{
|
||||
'fieldname': 'gst_vehicle_type',
|
||||
'label': 'GST Vehicle Type',
|
||||
'fieldtype': 'Select',
|
||||
'options': `Regular\nOver Dimensional Cargo (ODC)`,
|
||||
'depends_on': 'eval:(doc.mode_of_transport === "Road")',
|
||||
'default': frm.doc.gst_vehicle_type
|
||||
}
|
||||
]
|
||||
|
||||
const action = () => {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Generate E-Way Bill'),
|
||||
wide: 1,
|
||||
fields: fields,
|
||||
fields: get_ewaybill_fields(frm),
|
||||
primary_action: function() {
|
||||
const data = d.get_values();
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.generate_eway_bill',
|
||||
args: {
|
||||
docname: name, irn,
|
||||
doctype,
|
||||
docname: name,
|
||||
irn,
|
||||
...data
|
||||
},
|
||||
freeze: true,
|
||||
@@ -177,7 +102,7 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
d.show();
|
||||
};
|
||||
|
||||
frm.add_custom_button(__("Generate E-Way Bill"), action, __("E Invoicing"));
|
||||
add_custom_button(__("Generate E-Way Bill"), action);
|
||||
}
|
||||
|
||||
if (docstatus == 1 && irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
|
||||
@@ -206,6 +131,7 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill',
|
||||
args: {
|
||||
doctype,
|
||||
docname: name,
|
||||
eway_bill: ewaybill,
|
||||
reason: data.reason.split('-')[0],
|
||||
@@ -220,8 +146,94 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
||||
});
|
||||
d.show();
|
||||
};
|
||||
frm.add_custom_button(__("Cancel E-Way Bill"), action, __("E Invoicing"));
|
||||
add_custom_button(__("Cancel E-Way Bill"), action);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const get_ewaybill_fields = (frm) => {
|
||||
return [
|
||||
{
|
||||
'fieldname': 'transporter',
|
||||
'label': 'Transporter',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Supplier',
|
||||
'default': frm.doc.transporter
|
||||
},
|
||||
{
|
||||
'fieldname': 'gst_transporter_id',
|
||||
'label': 'GST Transporter ID',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'transporter.gst_transporter_id',
|
||||
'default': frm.doc.gst_transporter_id
|
||||
},
|
||||
{
|
||||
'fieldname': 'driver',
|
||||
'label': 'Driver',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Driver',
|
||||
'default': frm.doc.driver
|
||||
},
|
||||
{
|
||||
'fieldname': 'lr_no',
|
||||
'label': 'Transport Receipt No',
|
||||
'fieldtype': 'Data',
|
||||
'default': frm.doc.lr_no
|
||||
},
|
||||
{
|
||||
'fieldname': 'vehicle_no',
|
||||
'label': 'Vehicle No',
|
||||
'fieldtype': 'Data',
|
||||
'depends_on': 'eval:(doc.mode_of_transport === "Road")',
|
||||
'default': frm.doc.vehicle_no
|
||||
},
|
||||
{
|
||||
'fieldname': 'distance',
|
||||
'label': 'Distance (in km)',
|
||||
'fieldtype': 'Float',
|
||||
'default': frm.doc.distance
|
||||
},
|
||||
{
|
||||
'fieldname': 'transporter_col_break',
|
||||
'fieldtype': 'Column Break',
|
||||
},
|
||||
{
|
||||
'fieldname': 'transporter_name',
|
||||
'label': 'Transporter Name',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'transporter.name',
|
||||
'read_only': 1,
|
||||
'default': frm.doc.transporter_name
|
||||
},
|
||||
{
|
||||
'fieldname': 'mode_of_transport',
|
||||
'label': 'Mode of Transport',
|
||||
'fieldtype': 'Select',
|
||||
'options': `\nRoad\nAir\nRail\nShip`,
|
||||
'default': frm.doc.mode_of_transport
|
||||
},
|
||||
{
|
||||
'fieldname': 'driver_name',
|
||||
'label': 'Driver Name',
|
||||
'fieldtype': 'Data',
|
||||
'fetch_from': 'driver.full_name',
|
||||
'read_only': 1,
|
||||
'default': frm.doc.driver_name
|
||||
},
|
||||
{
|
||||
'fieldname': 'lr_date',
|
||||
'label': 'Transport Receipt Date',
|
||||
'fieldtype': 'Date',
|
||||
'default': frm.doc.lr_date
|
||||
},
|
||||
{
|
||||
'fieldname': 'gst_vehicle_type',
|
||||
'label': 'GST Vehicle Type',
|
||||
'fieldtype': 'Select',
|
||||
'options': `Regular\nOver Dimensional Cargo (ODC)`,
|
||||
'depends_on': 'eval:(doc.mode_of_transport === "Road")',
|
||||
'default': frm.doc.gst_vehicle_type
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -240,9 +240,7 @@ def get_eway_bill_details(invoice):
|
||||
vehicle_type=vehicle_type[invoice.gst_vehicle_type]
|
||||
))
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_einvoice(doctype, name):
|
||||
invoice = frappe.get_doc(doctype, name)
|
||||
def make_einvoice(invoice):
|
||||
schema = read_json('einv_template')
|
||||
|
||||
trans_details = get_trans_details(invoice)
|
||||
@@ -255,7 +253,7 @@ def make_einvoice(doctype, name):
|
||||
buyer_details = get_overseas_address_details(invoice.customer_address)
|
||||
else:
|
||||
buyer_details = get_party_details(invoice.customer_address)
|
||||
place_of_supply = get_place_of_supply(invoice, doctype) or invoice.billing_address_gstin
|
||||
place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
|
||||
place_of_supply = place_of_supply[:2]
|
||||
buyer_details.update(dict(place_of_supply=place_of_supply))
|
||||
|
||||
@@ -351,42 +349,10 @@ def validate_einvoice(validations, einvoice, errors=[]):
|
||||
|
||||
return errors
|
||||
|
||||
def update_invoice(doctype, docname, res):
|
||||
enc_signed_invoice = res.get('SignedInvoice')
|
||||
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
|
||||
|
||||
frappe.db.set_value(doctype, docname, 'irn', res.get('Irn'))
|
||||
frappe.db.set_value(doctype, docname, 'ewaybill', res.get('EwbNo'))
|
||||
frappe.db.set_value(doctype, docname, 'signed_einvoice', dec_signed_invoice)
|
||||
|
||||
signed_qr_code = res.get('SignedQRCode')
|
||||
frappe.db.set_value(doctype, docname, 'signed_qr_code', signed_qr_code)
|
||||
|
||||
attach_qrcode_image(doctype, docname, signed_qr_code)
|
||||
|
||||
def attach_qrcode_image(doctype, docname, qrcode):
|
||||
if not qrcode: return
|
||||
|
||||
_file = frappe.new_doc('File')
|
||||
_file.update({
|
||||
'file_name': f'QRCode_{docname}.png',
|
||||
'attached_to_doctype': doctype,
|
||||
'attached_to_name': docname,
|
||||
'content': 'qrcode',
|
||||
'is_private': 1
|
||||
})
|
||||
_file.insert()
|
||||
frappe.db.commit()
|
||||
url = qrcreate(qrcode)
|
||||
abs_file_path = os.path.abspath(_file.get_full_path())
|
||||
url.png(abs_file_path, scale=2)
|
||||
|
||||
frappe.db.set_value(doctype, docname, 'qrcode_image', _file.file_url)
|
||||
|
||||
class ResponseFailure(Exception): pass
|
||||
|
||||
class GSPConnector():
|
||||
def __init__(self):
|
||||
def __init__(self, doctype, docname):
|
||||
self.credentials = frappe.get_cached_doc('E Invoice Settings')
|
||||
|
||||
self.base_url = 'https://gsp.adaequare.com/'
|
||||
@@ -397,6 +363,8 @@ class GSPConnector():
|
||||
self.cancel_irn_url = self.base_url + 'test/enriched/ei/api/invoice/cancel'
|
||||
self.cancel_ewaybill_url = self.base_url + '/test/enriched/ei/api/ewayapi'
|
||||
self.generate_ewaybill_url = self.base_url + 'test/enriched/ei/api/ewaybill'
|
||||
|
||||
self.invoice = frappe.get_cached_doc(doctype, docname)
|
||||
|
||||
def get_auth_token(self):
|
||||
if time_diff_in_seconds(self.credentials.token_expiry, now_datetime()) < 150.0:
|
||||
@@ -473,23 +441,22 @@ class GSPConnector():
|
||||
frappe.cache().hset('gstin_cache', key, details)
|
||||
return details
|
||||
|
||||
def generate_irn(self, docname):
|
||||
def generate_irn(self):
|
||||
headers = self.get_headers()
|
||||
doctype = 'Sales Invoice'
|
||||
einvoice = make_einvoice(doctype, docname)
|
||||
einvoice = make_einvoice(self.invoice)
|
||||
data = json.dumps(einvoice)
|
||||
|
||||
try:
|
||||
res = make_post_request(self.generate_irn_url, headers=headers, data=data)
|
||||
if res.get('success'):
|
||||
update_invoice(doctype, docname, res.get('result'))
|
||||
self.set_einvoice_data(res.get('result'))
|
||||
|
||||
elif '2150' in res.get('message'):
|
||||
# IRN already generated
|
||||
irn = res.get('result')[0].get('Desc').get('Irn')
|
||||
irn_details = self.get_irn_details(irn)
|
||||
if irn_details:
|
||||
update_invoice(doctype, docname, irn_details)
|
||||
self.set_einvoice_data(irn_details)
|
||||
|
||||
else:
|
||||
self.log_error(res)
|
||||
@@ -521,9 +488,8 @@ class GSPConnector():
|
||||
self.log_error()
|
||||
self.raise_error(True)
|
||||
|
||||
def cancel_irn(self, docname, irn, reason, remark):
|
||||
def cancel_irn(self, irn, reason, remark):
|
||||
headers = self.get_headers()
|
||||
doctype = 'Sales Invoice'
|
||||
data = json.dumps({
|
||||
'Irn': irn,
|
||||
'Cnlrsn': reason,
|
||||
@@ -533,8 +499,14 @@ class GSPConnector():
|
||||
try:
|
||||
res = make_post_request(self.cancel_irn_url, headers=headers, data=data)
|
||||
if res.get('success'):
|
||||
frappe.db.set_value(doctype, docname, 'irn_cancelled', 1)
|
||||
# frappe.db.set_value(doctype, docname, 'cancelled_on', res.get('CancelDate'))
|
||||
self.invoice.irn_cancelled = 1
|
||||
self.invoice.flags.updater_reference = {
|
||||
'doctype': self.invoice.doctype,
|
||||
'docname': self.invoice.name,
|
||||
'label': _('IRN Cancelled - {}').format(remark)
|
||||
}
|
||||
self.update_invoice()
|
||||
|
||||
else:
|
||||
self.log_error(res)
|
||||
raise ResponseFailure
|
||||
@@ -550,8 +522,6 @@ class GSPConnector():
|
||||
args = frappe._dict(kwargs)
|
||||
|
||||
headers = self.get_headers()
|
||||
doctype = 'Sales Invoice'
|
||||
docname = args.docname
|
||||
eway_bill_details = get_eway_bill_details(args)
|
||||
data = json.dumps({
|
||||
'Irn': args.irn,
|
||||
@@ -568,12 +538,16 @@ class GSPConnector():
|
||||
try:
|
||||
res = make_post_request(self.generate_ewaybill_url, headers=headers, data=data)
|
||||
if res.get('success'):
|
||||
frappe.db.set_value(doctype, docname, 'ewaybill', res.get('result').get('EwbNo'))
|
||||
frappe.db.set_value(doctype, docname, 'eway_bill_cancelled', 0)
|
||||
for d in args:
|
||||
if d in ['docname', 'cmd']: continue
|
||||
# update eway bill details in sales invoice
|
||||
frappe.db.set_value(doctype, docname, d, args[d])
|
||||
self.invoice.ewaybill = res.get('result').get('EwbNo')
|
||||
self.invoice.eway_bill_cancelled = 0
|
||||
self.invoice.update(args)
|
||||
self.invoice.flags.updater_reference = {
|
||||
'doctype': self.invoice.doctype,
|
||||
'docname': self.invoice.name,
|
||||
'label': _('E-Way Bill Generated')
|
||||
}
|
||||
self.update_invoice()
|
||||
|
||||
else:
|
||||
self.log_error(res)
|
||||
raise ResponseFailure
|
||||
@@ -585,7 +559,7 @@ class GSPConnector():
|
||||
self.log_error(data)
|
||||
self.raise_error(True)
|
||||
|
||||
def cancel_eway_bill(self, docname, eway_bill, reason, remark):
|
||||
def cancel_eway_bill(self, eway_bill, reason, remark):
|
||||
headers = self.get_headers()
|
||||
doctype = 'Sales Invoice'
|
||||
data = json.dumps({
|
||||
@@ -597,8 +571,15 @@ class GSPConnector():
|
||||
try:
|
||||
res = make_post_request(self.cancel_ewaybill_url, headers=headers, data=data)
|
||||
if res.get('success'):
|
||||
frappe.db.set_value(doctype, docname, 'ewaybill', '')
|
||||
frappe.db.set_value(doctype, docname, 'eway_bill_cancelled', 1)
|
||||
self.invoice.ewaybill = ''
|
||||
self.invoice.eway_bill_cancelled = 1
|
||||
self.invoice.flags.updater_reference = {
|
||||
'doctype': self.invoice.doctype,
|
||||
'docname': self.invoice.name,
|
||||
'label': _('E-Way Bill Cancelled - {}').format(remark)
|
||||
}
|
||||
self.update_invoice()
|
||||
|
||||
else:
|
||||
self.log_error(res)
|
||||
raise ResponseFailure
|
||||
@@ -625,23 +606,67 @@ class GSPConnector():
|
||||
raise_exception=raise_exception,
|
||||
indicator='red'
|
||||
)
|
||||
|
||||
def set_einvoice_data(self, res):
|
||||
enc_signed_invoice = res.get('SignedInvoice')
|
||||
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
|
||||
|
||||
self.invoice.irn = res.get('Irn')
|
||||
self.invoice.ewaybill = res.get('EwbNo')
|
||||
self.invoice.signed_einvoice = dec_signed_invoice
|
||||
self.invoice.signed_qr_code = res.get('SignedQRCode')
|
||||
|
||||
self.attach_qrcode_image()
|
||||
|
||||
self.invoice.flags.updater_reference = {
|
||||
'doctype': self.invoice.doctype,
|
||||
'docname': self.invoice.name,
|
||||
'label': _('IRN Generated')
|
||||
}
|
||||
self.update_invoice()
|
||||
|
||||
def attach_qrcode_image(self):
|
||||
qrcode = self.invoice.signed_qr_code
|
||||
doctype = self.invoice.doctype
|
||||
docname = self.invoice.name
|
||||
|
||||
_file = frappe.new_doc('File')
|
||||
_file.update({
|
||||
'file_name': f'QRCode_{docname}.png',
|
||||
'attached_to_doctype': doctype,
|
||||
'attached_to_name': docname,
|
||||
'content': 'qrcode',
|
||||
'is_private': 1
|
||||
})
|
||||
_file.insert()
|
||||
frappe.db.commit()
|
||||
url = qrcreate(qrcode)
|
||||
abs_file_path = os.path.abspath(_file.get_full_path())
|
||||
url.png(abs_file_path, scale=2)
|
||||
|
||||
self.invoice.qrcode_image = _file.file_url
|
||||
|
||||
def update_invoice(self):
|
||||
self.invoice.flags.ignore_validate_update_after_submit = True
|
||||
self.invoice.flags.ignore_validate = True
|
||||
self.invoice.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_irn(docname):
|
||||
gsp_connector = GSPConnector()
|
||||
gsp_connector.generate_irn(docname)
|
||||
def generate_irn(doctype, docname):
|
||||
gsp_connector = GSPConnector(doctype, docname)
|
||||
gsp_connector.generate_irn()
|
||||
|
||||
@frappe.whitelist()
|
||||
def cancel_irn(docname, irn, reason, remark):
|
||||
gsp_connector = GSPConnector()
|
||||
gsp_connector.cancel_irn(docname, irn, reason, remark)
|
||||
def cancel_irn(doctype, docname, irn, reason, remark):
|
||||
gsp_connector = GSPConnector(doctype, docname)
|
||||
gsp_connector.cancel_irn(irn, reason, remark)
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_eway_bill(**kwargs):
|
||||
gsp_connector = GSPConnector()
|
||||
def generate_eway_bill(doctype, docname, **kwargs):
|
||||
gsp_connector = GSPConnector(doctype, docname)
|
||||
gsp_connector.generate_eway_bill(**kwargs)
|
||||
|
||||
@frappe.whitelist()
|
||||
def cancel_eway_bill(docname, eway_bill, reason, remark):
|
||||
gsp_connector = GSPConnector()
|
||||
gsp_connector.cancel_eway_bill(docname, eway_bill, reason, remark)
|
||||
def cancel_eway_bill(doctype, docname, eway_bill, reason, remark):
|
||||
gsp_connector = GSPConnector(doctype, docname)
|
||||
gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
|
||||
Reference in New Issue
Block a user