Merge branch 'develop' into shopping-cart
This commit is contained in:
commit
45db1c5d74
@ -23,7 +23,7 @@ def validate_einvoice_fields(doc):
|
|||||||
invalid_doctype = doc.doctype != 'Sales Invoice'
|
invalid_doctype = doc.doctype != 'Sales Invoice'
|
||||||
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
|
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
|
||||||
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
|
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
|
||||||
no_taxes_applied = len(doc.get('taxes', [])) == 0
|
no_taxes_applied = not doc.get('taxes')
|
||||||
|
|
||||||
if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
|
if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
|
||||||
return
|
return
|
||||||
@ -63,7 +63,7 @@ def get_transaction_details(invoice):
|
|||||||
elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
|
elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
|
||||||
elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
|
elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
|
||||||
|
|
||||||
if not supply_type:
|
if not supply_type:
|
||||||
rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
|
rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
|
||||||
frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
|
frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
|
||||||
title=_('Invalid Supply Type'))
|
title=_('Invalid Supply Type'))
|
||||||
@ -128,7 +128,7 @@ def get_gstin_details(gstin):
|
|||||||
if details:
|
if details:
|
||||||
frappe.local.gstin_cache[key] = details
|
frappe.local.gstin_cache[key] = details
|
||||||
return details
|
return details
|
||||||
|
|
||||||
if not details:
|
if not details:
|
||||||
return GSPConnector.get_gstin_details(gstin)
|
return GSPConnector.get_gstin_details(gstin)
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ def get_item_list(invoice):
|
|||||||
item.serial_no = ""
|
item.serial_no = ""
|
||||||
|
|
||||||
item = update_item_taxes(invoice, item)
|
item = update_item_taxes(invoice, item)
|
||||||
|
|
||||||
item.total_value = abs(
|
item.total_value = abs(
|
||||||
item.taxable_value + item.igst_amount + item.sgst_amount +
|
item.taxable_value + item.igst_amount + item.sgst_amount +
|
||||||
item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
|
item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
|
||||||
@ -232,9 +232,9 @@ def get_invoice_value_details(invoice):
|
|||||||
invoice_value_details.round_off = invoice.base_rounding_adjustment
|
invoice_value_details.round_off = invoice.base_rounding_adjustment
|
||||||
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
|
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
|
||||||
invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
|
invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
|
||||||
|
|
||||||
invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
|
invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
|
||||||
|
|
||||||
return invoice_value_details
|
return invoice_value_details
|
||||||
|
|
||||||
def update_invoice_taxes(invoice, invoice_value_details):
|
def update_invoice_taxes(invoice, invoice_value_details):
|
||||||
@ -251,13 +251,13 @@ def update_invoice_taxes(invoice, invoice_value_details):
|
|||||||
if t.account_head in gst_accounts.cess_account:
|
if t.account_head in gst_accounts.cess_account:
|
||||||
# using after discount amt since item also uses after discount amt for cess calc
|
# using after discount amt since item also uses after discount amt for cess calc
|
||||||
invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
|
invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
|
||||||
|
|
||||||
for tax_type in ['igst', 'cgst', 'sgst']:
|
for tax_type in ['igst', 'cgst', 'sgst']:
|
||||||
if t.account_head in gst_accounts[f'{tax_type}_account']:
|
if t.account_head in gst_accounts[f'{tax_type}_account']:
|
||||||
invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
|
invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
|
||||||
else:
|
else:
|
||||||
invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
|
invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
|
||||||
|
|
||||||
return invoice_value_details
|
return invoice_value_details
|
||||||
|
|
||||||
def get_payment_details(invoice):
|
def get_payment_details(invoice):
|
||||||
@ -329,23 +329,23 @@ def make_einvoice(invoice):
|
|||||||
place_of_supply = get_place_of_supply(invoice, 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]
|
place_of_supply = place_of_supply[:2]
|
||||||
buyer_details.update(dict(place_of_supply=place_of_supply))
|
buyer_details.update(dict(place_of_supply=place_of_supply))
|
||||||
|
|
||||||
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
|
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
|
||||||
if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
|
if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
|
||||||
if invoice.gst_category == 'Overseas':
|
if invoice.gst_category == 'Overseas':
|
||||||
shipping_details = get_overseas_address_details(invoice.shipping_address_name)
|
shipping_details = get_overseas_address_details(invoice.shipping_address_name)
|
||||||
else:
|
else:
|
||||||
shipping_details = get_party_details(invoice.shipping_address_name)
|
shipping_details = get_party_details(invoice.shipping_address_name)
|
||||||
|
|
||||||
if invoice.is_pos and invoice.base_paid_amount:
|
if invoice.is_pos and invoice.base_paid_amount:
|
||||||
payment_details = get_payment_details(invoice)
|
payment_details = get_payment_details(invoice)
|
||||||
|
|
||||||
if invoice.is_return and invoice.return_against:
|
if invoice.is_return and invoice.return_against:
|
||||||
prev_doc_details = get_return_doc_reference(invoice)
|
prev_doc_details = get_return_doc_reference(invoice)
|
||||||
|
|
||||||
if invoice.transporter:
|
if invoice.transporter:
|
||||||
eway_bill_details = get_eway_bill_details(invoice)
|
eway_bill_details = get_eway_bill_details(invoice)
|
||||||
|
|
||||||
# not yet implemented
|
# not yet implemented
|
||||||
dispatch_details = period_details = export_details = frappe._dict({})
|
dispatch_details = period_details = export_details = frappe._dict({})
|
||||||
|
|
||||||
@ -357,7 +357,7 @@ def make_einvoice(invoice):
|
|||||||
export_details=export_details, eway_bill_details=eway_bill_details
|
export_details=export_details, eway_bill_details=eway_bill_details
|
||||||
)
|
)
|
||||||
einvoice = json.loads(einvoice)
|
einvoice = json.loads(einvoice)
|
||||||
|
|
||||||
validations = json.loads(read_json('einv_validation'))
|
validations = json.loads(read_json('einv_validation'))
|
||||||
errors = validate_einvoice(validations, einvoice)
|
errors = validate_einvoice(validations, einvoice)
|
||||||
if errors:
|
if errors:
|
||||||
@ -419,7 +419,7 @@ def validate_einvoice(validations, einvoice, errors=[]):
|
|||||||
errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
|
errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
|
||||||
if pattern_str and not pattern.match(value):
|
if pattern_str and not pattern.match(value):
|
||||||
errors.append(field_validation.get('validationMsg'))
|
errors.append(field_validation.get('validationMsg'))
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
class RequestFailed(Exception): pass
|
class RequestFailed(Exception): pass
|
||||||
@ -452,19 +452,19 @@ class GSPConnector():
|
|||||||
else:
|
else:
|
||||||
credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
|
credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
|
||||||
return credentials
|
return credentials
|
||||||
|
|
||||||
def get_seller_gstin(self):
|
def get_seller_gstin(self):
|
||||||
gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
|
gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
|
||||||
if not gstin:
|
if not gstin:
|
||||||
frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
|
frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
|
||||||
return gstin
|
return gstin
|
||||||
|
|
||||||
def get_auth_token(self):
|
def get_auth_token(self):
|
||||||
if time_diff_in_seconds(self.e_invoice_settings.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()
|
self.fetch_auth_token()
|
||||||
|
|
||||||
return self.e_invoice_settings.auth_token
|
return self.e_invoice_settings.auth_token
|
||||||
|
|
||||||
def make_request(self, request_type, url, headers=None, data=None):
|
def make_request(self, request_type, url, headers=None, data=None):
|
||||||
if request_type == 'post':
|
if request_type == 'post':
|
||||||
res = make_post_request(url, headers=headers, data=data)
|
res = make_post_request(url, headers=headers, data=data)
|
||||||
@ -473,7 +473,7 @@ class GSPConnector():
|
|||||||
|
|
||||||
self.log_request(url, headers, data, res)
|
self.log_request(url, headers, data, res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def log_request(self, url, headers, data, res):
|
def log_request(self, url, headers, data, res):
|
||||||
headers.update({ 'password': self.credentials.password })
|
headers.update({ 'password': self.credentials.password })
|
||||||
request_log = frappe.get_doc({
|
request_log = frappe.get_doc({
|
||||||
@ -504,7 +504,7 @@ class GSPConnector():
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log_error(res)
|
self.log_error(res)
|
||||||
self.raise_error(True)
|
self.raise_error(True)
|
||||||
|
|
||||||
def get_headers(self):
|
def get_headers(self):
|
||||||
return {
|
return {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
@ -526,7 +526,7 @@ class GSPConnector():
|
|||||||
else:
|
else:
|
||||||
self.log_error(res)
|
self.log_error(res)
|
||||||
raise RequestFailed
|
raise RequestFailed
|
||||||
|
|
||||||
except RequestFailed:
|
except RequestFailed:
|
||||||
self.raise_error()
|
self.raise_error()
|
||||||
|
|
||||||
@ -571,7 +571,7 @@ class GSPConnector():
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
raise RequestFailed
|
raise RequestFailed
|
||||||
|
|
||||||
except RequestFailed:
|
except RequestFailed:
|
||||||
errors = self.sanitize_error_message(res.get('message'))
|
errors = self.sanitize_error_message(res.get('message'))
|
||||||
self.raise_error(errors=errors)
|
self.raise_error(errors=errors)
|
||||||
@ -579,7 +579,7 @@ class GSPConnector():
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log_error(data)
|
self.log_error(data)
|
||||||
self.raise_error(True)
|
self.raise_error(True)
|
||||||
|
|
||||||
def get_irn_details(self, irn):
|
def get_irn_details(self, irn):
|
||||||
headers = self.get_headers()
|
headers = self.get_headers()
|
||||||
|
|
||||||
@ -590,7 +590,7 @@ class GSPConnector():
|
|||||||
return res.get('result')
|
return res.get('result')
|
||||||
else:
|
else:
|
||||||
raise RequestFailed
|
raise RequestFailed
|
||||||
|
|
||||||
except RequestFailed:
|
except RequestFailed:
|
||||||
errors = self.sanitize_error_message(res.get('message'))
|
errors = self.sanitize_error_message(res.get('message'))
|
||||||
self.raise_error(errors=errors)
|
self.raise_error(errors=errors)
|
||||||
@ -598,7 +598,7 @@ class GSPConnector():
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log_error()
|
self.log_error()
|
||||||
self.raise_error(True)
|
self.raise_error(True)
|
||||||
|
|
||||||
def cancel_irn(self, irn, reason, remark):
|
def cancel_irn(self, irn, reason, remark):
|
||||||
headers = self.get_headers()
|
headers = self.get_headers()
|
||||||
data = json.dumps({
|
data = json.dumps({
|
||||||
@ -620,7 +620,7 @@ class GSPConnector():
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
raise RequestFailed
|
raise RequestFailed
|
||||||
|
|
||||||
except RequestFailed:
|
except RequestFailed:
|
||||||
errors = self.sanitize_error_message(res.get('message'))
|
errors = self.sanitize_error_message(res.get('message'))
|
||||||
self.raise_error(errors=errors)
|
self.raise_error(errors=errors)
|
||||||
@ -669,7 +669,7 @@ class GSPConnector():
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log_error(data)
|
self.log_error(data)
|
||||||
self.raise_error(True)
|
self.raise_error(True)
|
||||||
|
|
||||||
def cancel_eway_bill(self, eway_bill, reason, remark):
|
def cancel_eway_bill(self, eway_bill, reason, remark):
|
||||||
headers = self.get_headers()
|
headers = self.get_headers()
|
||||||
data = json.dumps({
|
data = json.dumps({
|
||||||
@ -701,7 +701,7 @@ class GSPConnector():
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.log_error(data)
|
self.log_error(data)
|
||||||
self.raise_error(True)
|
self.raise_error(True)
|
||||||
|
|
||||||
def sanitize_error_message(self, message):
|
def sanitize_error_message(self, message):
|
||||||
'''
|
'''
|
||||||
On validation errors, response message looks something like this:
|
On validation errors, response message looks something like this:
|
||||||
@ -740,7 +740,7 @@ class GSPConnector():
|
|||||||
"Exception:", err_tb
|
"Exception:", err_tb
|
||||||
])
|
])
|
||||||
frappe.log_error(title=_('E Invoice Request Failed'), message=message)
|
frappe.log_error(title=_('E Invoice Request Failed'), message=message)
|
||||||
|
|
||||||
def raise_error(self, raise_exception=False, errors=[]):
|
def raise_error(self, raise_exception=False, errors=[]):
|
||||||
title = _('E Invoice Request Failed')
|
title = _('E Invoice Request Failed')
|
||||||
if errors:
|
if errors:
|
||||||
@ -753,7 +753,7 @@ class GSPConnector():
|
|||||||
raise_exception=raise_exception,
|
raise_exception=raise_exception,
|
||||||
indicator='red'
|
indicator='red'
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_einvoice_data(self, res):
|
def set_einvoice_data(self, res):
|
||||||
enc_signed_invoice = res.get('SignedInvoice')
|
enc_signed_invoice = res.get('SignedInvoice')
|
||||||
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
|
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
|
||||||
@ -792,7 +792,7 @@ class GSPConnector():
|
|||||||
_file.save()
|
_file.save()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
self.invoice.qrcode_image = _file.file_url
|
self.invoice.qrcode_image = _file.file_url
|
||||||
|
|
||||||
def update_invoice(self):
|
def update_invoice(self):
|
||||||
self.invoice.flags.ignore_validate_update_after_submit = True
|
self.invoice.flags.ignore_validate_update_after_submit = True
|
||||||
self.invoice.flags.ignore_validate = True
|
self.invoice.flags.ignore_validate = True
|
||||||
|
Loading…
Reference in New Issue
Block a user