Merge branch 'develop' into fix-scrap-items-updation
This commit is contained in:
		
						commit
						6106a635f8
					
				| @ -533,8 +533,8 @@ frappe.ui.form.on('Payment Entry', { | ||||
| 	source_exchange_rate: function(frm) { | ||||
| 		if (frm.doc.paid_amount) { | ||||
| 			frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); | ||||
| 			if(!frm.set_paid_amount_based_on_received_amount && | ||||
| 					(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) { | ||||
| 			// target exchange rate should always be same as source if both account currencies is same
 | ||||
| 			if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { | ||||
| 				frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); | ||||
| 				frm.set_value("base_received_amount", frm.doc.base_paid_amount); | ||||
| 			} | ||||
|  | ||||
| @ -55,14 +55,17 @@ class PaymentEntry(AccountsController): | ||||
| 		self.validate_mandatory() | ||||
| 		self.validate_reference_documents() | ||||
| 		self.set_tax_withholding() | ||||
| 		self.apply_taxes() | ||||
| 		self.set_amounts() | ||||
| 		self.validate_amounts() | ||||
| 		self.apply_taxes() | ||||
| 		self.set_amounts_after_tax() | ||||
| 		self.clear_unallocated_reference_document_rows() | ||||
| 		self.validate_payment_against_negative_invoice() | ||||
| 		self.validate_transaction_reference() | ||||
| 		self.set_title() | ||||
| 		self.set_remarks() | ||||
| 		self.validate_duplicate_entry() | ||||
| 		self.validate_payment_type_with_outstanding() | ||||
| 		self.validate_allocated_amount() | ||||
| 		self.validate_paid_invoices() | ||||
| 		self.ensure_supplier_is_not_blocked() | ||||
| @ -118,6 +121,11 @@ class PaymentEntry(AccountsController): | ||||
| 			if not self.get(field): | ||||
| 				self.set(field, bank_data.account) | ||||
| 
 | ||||
| 	def validate_payment_type_with_outstanding(self): | ||||
| 		total_outstanding = sum(d.allocated_amount for d in self.get('references')) | ||||
| 		if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive': | ||||
| 			frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type")) | ||||
| 
 | ||||
| 	def validate_allocated_amount(self): | ||||
| 		for d in self.get("references"): | ||||
| 			if (flt(d.allocated_amount))> 0: | ||||
| @ -236,7 +244,9 @@ class PaymentEntry(AccountsController): | ||||
| 						self.company_currency, self.posting_date) | ||||
| 
 | ||||
| 	def set_target_exchange_rate(self, ref_doc=None): | ||||
| 		if self.paid_to and not self.target_exchange_rate: | ||||
| 		if self.paid_from_account_currency == self.paid_to_account_currency: | ||||
| 			self.target_exchange_rate = self.source_exchange_rate | ||||
| 		elif self.paid_to and not self.target_exchange_rate: | ||||
| 			if ref_doc: | ||||
| 				if self.paid_to_account_currency == ref_doc.currency: | ||||
| 					self.target_exchange_rate = ref_doc.get("exchange_rate") | ||||
| @ -468,13 +478,22 @@ class PaymentEntry(AccountsController): | ||||
| 	def set_amounts(self): | ||||
| 		self.set_received_amount() | ||||
| 		self.set_amounts_in_company_currency() | ||||
| 		self.set_amounts_after_tax() | ||||
| 		self.set_total_allocated_amount() | ||||
| 		self.set_unallocated_amount() | ||||
| 		self.set_difference_amount() | ||||
| 
 | ||||
| 	def validate_amounts(self): | ||||
| 		self.validate_received_amount() | ||||
| 	 | ||||
| 	def validate_received_amount(self): | ||||
| 		if self.paid_from_account_currency == self.paid_to_account_currency: | ||||
| 			if self.paid_amount != self.received_amount: | ||||
| 				frappe.throw(_("Received Amount cannot be greater than Paid Amount")) | ||||
| 
 | ||||
| 	def set_received_amount(self): | ||||
| 		self.base_received_amount = self.base_paid_amount | ||||
| 		if self.paid_from_account_currency == self.paid_to_account_currency: | ||||
| 			self.received_amount = self.paid_amount | ||||
| 
 | ||||
| 	def set_amounts_after_tax(self): | ||||
| 		applicable_tax = 0 | ||||
|  | ||||
| @ -107,7 +107,7 @@ class TestPaymentEntry(unittest.TestCase): | ||||
| 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") | ||||
| 		pe.reference_no = "1" | ||||
| 		pe.reference_date = "2016-01-01" | ||||
| 		pe.target_exchange_rate = 50 | ||||
| 		pe.source_exchange_rate = 50 | ||||
| 		pe.insert() | ||||
| 		pe.submit() | ||||
| 
 | ||||
| @ -154,7 +154,7 @@ class TestPaymentEntry(unittest.TestCase): | ||||
| 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") | ||||
| 		pe.reference_no = "1" | ||||
| 		pe.reference_date = "2016-01-01" | ||||
| 		pe.target_exchange_rate = 50 | ||||
| 		pe.source_exchange_rate = 50 | ||||
| 		pe.insert() | ||||
| 		pe.submit() | ||||
| 
 | ||||
| @ -491,7 +491,7 @@ class TestPaymentEntry(unittest.TestCase): | ||||
| 		pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") | ||||
| 		pe.reference_no = "1" | ||||
| 		pe.reference_date = "2016-01-01" | ||||
| 		pe.target_exchange_rate = 55 | ||||
| 		pe.source_exchange_rate = 55 | ||||
| 
 | ||||
| 		pe.append("deductions", { | ||||
| 			"account": "_Test Exchange Gain/Loss - _TC", | ||||
|  | ||||
| @ -111,16 +111,12 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex | ||||
| 	} | ||||
| 
 | ||||
| 	write_off_outstanding_amount_automatically() { | ||||
| 		if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { | ||||
| 		if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { | ||||
| 			frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); | ||||
| 			// this will make outstanding amount 0
 | ||||
| 			this.frm.set_value("write_off_amount", | ||||
| 				flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) | ||||
| 			); | ||||
| 			this.frm.toggle_enable("write_off_amount", false); | ||||
| 
 | ||||
| 		} else { | ||||
| 			this.frm.toggle_enable("write_off_amount", true); | ||||
| 		} | ||||
| 
 | ||||
| 		this.calculate_outstanding_amount(false); | ||||
|  | ||||
| @ -595,7 +595,8 @@ | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode" | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 1, | ||||
| @ -1182,7 +1183,8 @@ | ||||
|    "label": "Write Off Amount", | ||||
|    "no_copy": 1, | ||||
|    "options": "currency", | ||||
|    "print_hide": 1 | ||||
|    "print_hide": 1, | ||||
|    "read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "base_write_off_amount", | ||||
| @ -1553,7 +1555,7 @@ | ||||
|  "icon": "fa fa-file-text", | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-07-29 13:37:20.636171", | ||||
|  "modified": "2021-08-18 16:13:52.080543", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "POS Invoice", | ||||
|  | ||||
| @ -106,7 +106,6 @@ | ||||
|    "depends_on": "eval:doc.rate_or_discount==\"Rate\"", | ||||
|    "fieldname": "rate", | ||||
|    "fieldtype": "Currency", | ||||
|    "in_list_view": 1, | ||||
|    "label": "Rate" | ||||
|   }, | ||||
|   { | ||||
| @ -170,7 +169,7 @@ | ||||
|  "index_web_pages_for_search": 1, | ||||
|  "istable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-03-07 11:56:23.424137", | ||||
|  "modified": "2021-08-19 15:49:29.598727", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "Promotional Scheme Price Discount", | ||||
|  | ||||
| @ -668,8 +668,7 @@ | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode", | ||||
|    "show_days": 1, | ||||
|    "show_seconds": 1 | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 1, | ||||
| @ -1715,7 +1714,7 @@ | ||||
|  "idx": 204, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-08-07 17:53:14.351439", | ||||
|  "modified": "2021-08-17 20:16:12.737743", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "Purchase Invoice", | ||||
|  | ||||
| @ -1,8 +1,6 @@ | ||||
| {% include "erpnext/regional/india/taxes.js" %} | ||||
| {% include "erpnext/regional/india/e_invoice/einvoice.js" %} | ||||
| 
 | ||||
| erpnext.setup_auto_gst_taxation('Sales Invoice'); | ||||
| erpnext.setup_einvoice_actions('Sales Invoice') | ||||
| 
 | ||||
| frappe.ui.form.on("Sales Invoice", { | ||||
| 	setup: function(frm) { | ||||
|  | ||||
| @ -36,139 +36,4 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { | ||||
| 	}; | ||||
| 
 | ||||
| 	list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false); | ||||
| 
 | ||||
| 	const generate_irns = () => { | ||||
| 		const docnames = list_view.get_checked_items(true); | ||||
| 		if (docnames && docnames.length) { | ||||
| 			frappe.call({ | ||||
| 				method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices', | ||||
| 				args: { docnames }, | ||||
| 				freeze: true, | ||||
| 				freeze_message: __('Generating E-Invoices...') | ||||
| 			}); | ||||
| 		} else { | ||||
| 			frappe.msgprint({ | ||||
| 				message: __('Please select at least one sales invoice to generate IRN'), | ||||
| 				title: __('No Invoice Selected'), | ||||
| 				indicator: 'red' | ||||
| 			}); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const cancel_irns = () => { | ||||
| 		const docnames = list_view.get_checked_items(true); | ||||
| 
 | ||||
| 		const fields = [ | ||||
| 			{ | ||||
| 				"label": "Reason", | ||||
| 				"fieldname": "reason", | ||||
| 				"fieldtype": "Select", | ||||
| 				"reqd": 1, | ||||
| 				"default": "1-Duplicate", | ||||
| 				"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] | ||||
| 			}, | ||||
| 			{ | ||||
| 				"label": "Remark", | ||||
| 				"fieldname": "remark", | ||||
| 				"fieldtype": "Data", | ||||
| 				"reqd": 1 | ||||
| 			} | ||||
| 		]; | ||||
| 
 | ||||
| 		const d = new frappe.ui.Dialog({ | ||||
| 			title: __("Cancel IRN"), | ||||
| 			fields: fields, | ||||
| 			primary_action: function() { | ||||
| 				const data = d.get_values(); | ||||
| 				frappe.call({ | ||||
| 					method: 'erpnext.regional.india.e_invoice.utils.cancel_irns', | ||||
| 					args: { | ||||
| 						doctype: list_view.doctype, | ||||
| 						docnames, | ||||
| 						reason: data.reason.split('-')[0], | ||||
| 						remark: data.remark | ||||
| 					}, | ||||
| 					freeze: true, | ||||
| 					freeze_message: __('Cancelling E-Invoices...'), | ||||
| 				}); | ||||
| 				d.hide(); | ||||
| 			}, | ||||
| 			primary_action_label: __('Submit') | ||||
| 		}); | ||||
| 		d.show(); | ||||
| 	}; | ||||
| 
 | ||||
| 	let einvoicing_enabled = false; | ||||
| 	frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => { | ||||
| 		einvoicing_enabled = enabled; | ||||
| 	}); | ||||
| 
 | ||||
| 	list_view.$result.on("change", "input[type=checkbox]", () => { | ||||
| 		if (einvoicing_enabled) { | ||||
| 			const docnames = list_view.get_checked_items(true); | ||||
| 			// show/hide e-invoicing actions when no sales invoices are checked
 | ||||
| 			if (docnames && docnames.length) { | ||||
| 				// prevent adding actions twice if e-invoicing action group already exists
 | ||||
| 				if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) { | ||||
| 					list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing')); | ||||
| 					list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing')); | ||||
| 				} | ||||
| 			} else { | ||||
| 				list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing')); | ||||
| 				list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing')); | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	frappe.realtime.on("bulk_einvoice_generation_complete", (data) => { | ||||
| 		const { failures, user, invoices } = data; | ||||
| 
 | ||||
| 		if (invoices.length != failures.length) { | ||||
| 			frappe.msgprint({ | ||||
| 				message: __('{0} e-invoices generated successfully', [invoices.length]), | ||||
| 				title: __('Bulk E-Invoice Generation Complete'), | ||||
| 				indicator: 'orange' | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		if (failures && failures.length && user == frappe.session.user) { | ||||
| 			let message = ` | ||||
| 				Failed to generate IRNs for following ${failures.length} sales invoices: | ||||
| 				<ul style="padding-left: 20px; padding-top: 5px;"> | ||||
| 					${failures.map(d => `<li>${d.docname}</li>`).join('')} | ||||
| 				</ul> | ||||
| 			`;
 | ||||
| 			frappe.msgprint({ | ||||
| 				message: message, | ||||
| 				title: __('Bulk E-Invoice Generation Complete'), | ||||
| 				indicator: 'orange' | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => { | ||||
| 		const { failures, user, invoices } = data; | ||||
| 
 | ||||
| 		if (invoices.length != failures.length) { | ||||
| 			frappe.msgprint({ | ||||
| 				message: __('{0} e-invoices cancelled successfully', [invoices.length]), | ||||
| 				title: __('Bulk E-Invoice Cancellation Complete'), | ||||
| 				indicator: 'orange' | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		if (failures && failures.length && user == frappe.session.user) { | ||||
| 			let message = ` | ||||
| 				Failed to cancel IRNs for following ${failures.length} sales invoices: | ||||
| 				<ul style="padding-left: 20px; padding-top: 5px;"> | ||||
| 					${failures.map(d => `<li>${d.docname}</li>`).join('')} | ||||
| 				</ul> | ||||
| 			`;
 | ||||
| 			frappe.msgprint({ | ||||
| 				message: message, | ||||
| 				title: __('Bulk E-Invoice Cancellation Complete'), | ||||
| 				indicator: 'orange' | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| @ -324,16 +324,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e | ||||
| 	} | ||||
| 
 | ||||
| 	write_off_outstanding_amount_automatically() { | ||||
| 		if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { | ||||
| 		if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { | ||||
| 			frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); | ||||
| 			// this will make outstanding amount 0
 | ||||
| 			this.frm.set_value("write_off_amount", | ||||
| 				flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) | ||||
| 			); | ||||
| 			this.frm.toggle_enable("write_off_amount", false); | ||||
| 
 | ||||
| 		} else { | ||||
| 			this.frm.toggle_enable("write_off_amount", true); | ||||
| 		} | ||||
| 
 | ||||
| 		this.calculate_outstanding_amount(false); | ||||
| @ -787,8 +783,6 @@ frappe.ui.form.on('Sales Invoice', { | ||||
| 		if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); | ||||
| 		else hide_field(['c_form_applicable', 'c_form_no']); | ||||
| 
 | ||||
| 		frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically)); | ||||
| 
 | ||||
| 		frm.refresh_fields(); | ||||
| 	}, | ||||
| 
 | ||||
|  | ||||
| @ -692,6 +692,7 @@ | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "options": "Barcode", | ||||
|    "hide_days": 1, | ||||
|    "hide_seconds": 1, | ||||
|    "label": "Scan Barcode" | ||||
| @ -1443,7 +1444,8 @@ | ||||
|    "label": "Write Off Amount", | ||||
|    "no_copy": 1, | ||||
|    "options": "currency", | ||||
|    "print_hide": 1 | ||||
|    "print_hide": 1, | ||||
|    "read_only_depends_on": "eval:doc.write_off_outstanding_amount_automatically" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "base_write_off_amount", | ||||
| @ -2013,7 +2015,7 @@ | ||||
|    "link_fieldname": "consolidated_invoice" | ||||
|   } | ||||
|  ], | ||||
|  "modified": "2021-08-17 19:00:32.230701", | ||||
|  "modified": "2021-08-18 16:07:45.122570", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "Sales Invoice", | ||||
|  | ||||
| @ -285,8 +285,6 @@ class SalesInvoice(SellingController): | ||||
| 
 | ||||
| 	def before_cancel(self): | ||||
| 		self.check_if_consolidated_invoice() | ||||
| 
 | ||||
| 		super(SalesInvoice, self).before_cancel() | ||||
| 		self.update_time_sheet(None) | ||||
| 
 | ||||
| 	def on_cancel(self): | ||||
|  | ||||
| @ -26,6 +26,7 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry | ||||
| from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt | ||||
| from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice | ||||
| from erpnext.stock.utils import get_incoming_rate | ||||
| from erpnext.accounts.utils import PaymentEntryUnlinkError | ||||
| 
 | ||||
| class TestSalesInvoice(unittest.TestCase): | ||||
| 	def make(self): | ||||
| @ -136,7 +137,7 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 		pe.paid_to_account_currency = si.currency | ||||
| 		pe.source_exchange_rate = 1 | ||||
| 		pe.target_exchange_rate = 1 | ||||
| 		pe.paid_amount = si.grand_total | ||||
| 		pe.paid_amount = si.outstanding_amount | ||||
| 		pe.insert() | ||||
| 		pe.submit() | ||||
| 
 | ||||
| @ -145,6 +146,42 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 		self.assertRaises(frappe.LinkExistsError, si.cancel) | ||||
| 		unlink_payment_on_cancel_of_invoice() | ||||
| 
 | ||||
| 	def test_payment_entry_unlink_against_standalone_credit_note(self): | ||||
| 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry | ||||
| 		si1 = create_sales_invoice(rate=1000) | ||||
| 		si2 = create_sales_invoice(rate=300) | ||||
| 		si3 = create_sales_invoice(qty=-1, rate=300, is_return=1) | ||||
| 		 | ||||
| 
 | ||||
| 		pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC") | ||||
| 		pe.append('references', { | ||||
| 			'reference_doctype': 'Sales Invoice', | ||||
| 			'reference_name': si2.name, | ||||
| 			'total_amount': si2.grand_total, | ||||
| 			'outstanding_amount': si2.outstanding_amount, | ||||
| 			'allocated_amount': si2.outstanding_amount | ||||
| 		}) | ||||
| 
 | ||||
| 		pe.append('references', { | ||||
| 			'reference_doctype': 'Sales Invoice', | ||||
| 			'reference_name': si3.name, | ||||
| 			'total_amount': si3.grand_total, | ||||
| 			'outstanding_amount': si3.outstanding_amount, | ||||
| 			'allocated_amount': si3.outstanding_amount | ||||
| 		}) | ||||
| 
 | ||||
| 		pe.reference_no = 'Test001' | ||||
| 		pe.reference_date = nowdate() | ||||
| 		pe.save() | ||||
| 		pe.submit() | ||||
| 
 | ||||
| 		si2.load_from_db() | ||||
| 		si2.cancel() | ||||
| 
 | ||||
| 		si1.load_from_db() | ||||
| 		self.assertRaises(PaymentEntryUnlinkError, si1.cancel) | ||||
| 
 | ||||
| 
 | ||||
| 	def test_sales_invoice_calculation_export_currency(self): | ||||
| 		si = frappe.copy_doc(test_records[2]) | ||||
| 		si.currency = "USD" | ||||
| @ -2014,7 +2051,7 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 
 | ||||
| 		data = get_ewb_data("Sales Invoice", [si.name]) | ||||
| 
 | ||||
| 		self.assertEqual(data['version'], '1.0.1118') | ||||
| 		self.assertEqual(data['version'], '1.0.0421') | ||||
| 		self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') | ||||
| 		self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company') | ||||
| 		self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer') | ||||
| @ -2027,54 +2064,6 @@ class TestSalesInvoice(unittest.TestCase): | ||||
| 		self.assertEqual(data['billLists'][0]['actualFromStateCode'],7) | ||||
| 		self.assertEqual(data['billLists'][0]['fromStateCode'],27) | ||||
| 
 | ||||
| 	def test_einvoice_submission_without_irn(self): | ||||
| 		# init | ||||
| 		einvoice_settings = frappe.get_doc('E Invoice Settings') | ||||
| 		einvoice_settings.enable = 1 | ||||
| 		einvoice_settings.applicable_from = nowdate() | ||||
| 		einvoice_settings.append('credentials', { | ||||
| 			'company': '_Test Company', | ||||
| 			'gstin': '27AAECE4835E1ZR', | ||||
| 			'username': 'test', | ||||
| 			'password': 'test' | ||||
| 		}) | ||||
| 		einvoice_settings.save() | ||||
| 
 | ||||
| 		country = frappe.flags.country | ||||
| 		frappe.flags.country = 'India' | ||||
| 
 | ||||
| 		si = make_sales_invoice_for_ewaybill() | ||||
| 		self.assertRaises(frappe.ValidationError, si.submit) | ||||
| 
 | ||||
| 		si.irn = 'test_irn' | ||||
| 		si.submit() | ||||
| 
 | ||||
| 		# reset | ||||
| 		einvoice_settings = frappe.get_doc('E Invoice Settings') | ||||
| 		einvoice_settings.enable = 0 | ||||
| 		frappe.flags.country = country | ||||
| 
 | ||||
| 	def test_einvoice_json(self): | ||||
| 		from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals | ||||
| 
 | ||||
| 		si = get_sales_invoice_for_e_invoice() | ||||
| 		si.discount_amount = 100 | ||||
| 		si.save() | ||||
| 
 | ||||
| 		einvoice = make_einvoice(si) | ||||
| 		self.assertTrue(einvoice['EwbDtls']) | ||||
| 		validate_totals(einvoice) | ||||
| 
 | ||||
| 		si.apply_discount_on = 'Net Total' | ||||
| 		si.save() | ||||
| 		einvoice = make_einvoice(si) | ||||
| 		validate_totals(einvoice) | ||||
| 
 | ||||
| 		[d.set('included_in_print_rate', 1) for d in si.taxes] | ||||
| 		si.save() | ||||
| 		einvoice = make_einvoice(si) | ||||
| 		validate_totals(einvoice) | ||||
| 
 | ||||
| 	def test_item_tax_net_range(self): | ||||
| 		item = create_item("T Shirt") | ||||
| 
 | ||||
|  | ||||
| @ -1,162 +0,0 @@ | ||||
| {%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%} | ||||
| {%- set einvoice = json.loads(doc.signed_einvoice) -%} | ||||
| 
 | ||||
| <div class="page-break"> | ||||
| 	<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}> | ||||
| 		{% if letter_head and not no_letterhead %} | ||||
| 			<div class="letter-head">{{ letter_head }}</div> | ||||
| 		{% endif %} | ||||
| 		<div class="print-heading"> | ||||
| 			<h2>E Invoice<br><small>{{ doc.name }}</small></h2> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	{% if print_settings.repeat_header_footer %} | ||||
| 	<div id="footer-html" class="visible-pdf"> | ||||
| 		{% if not no_letterhead and footer %} | ||||
| 		<div class="letter-head-footer"> | ||||
| 			{{ footer }} | ||||
| 		</div> | ||||
| 		{% endif %} | ||||
| 		<p class="text-center small page-number visible-pdf"> | ||||
| 			{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }} | ||||
| 		</p> | ||||
| 	</div> | ||||
| 	{% endif %} | ||||
| 	<h5 class="font-bold" style="margin-top: 0px;">1. Transaction Details</h5> | ||||
| 	<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;"> | ||||
| 		<div class="col-xs-8 column-break"> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>IRN</label></div> | ||||
| 				<div class="col-xs-8 value">{{ einvoice.Irn }}</div> | ||||
| 			</div> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>Ack. No</label></div> | ||||
| 				<div class="col-xs-8 value">{{ einvoice.AckNo }}</div> | ||||
| 			</div> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>Ack. Date</label></div> | ||||
| 				<div class="col-xs-8 value">{{ frappe.utils.format_datetime(einvoice.AckDt, "dd/MM/yyyy hh:mm:ss") }}</div> | ||||
| 			</div> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>Category</label></div> | ||||
| 				<div class="col-xs-8 value">{{ einvoice.TranDtls.SupTyp }}</div> | ||||
| 			</div> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>Document Type</label></div> | ||||
| 				<div class="col-xs-8 value">{{ einvoice.DocDtls.Typ }}</div> | ||||
| 			</div> | ||||
| 			<div class="row data-field"> | ||||
| 				<div class="col-xs-4"><label>Document No</label></div> | ||||
| 				<div class="col-xs-8 value">{{ einvoice.DocDtls.No }}</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="col-xs-4 column-break"> | ||||
| 			<img src="{{ doc.qrcode_image }}" width="175px" style="float: right;"> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">2. Party Details</h5> | ||||
| 	<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;"> | ||||
| 		{%- set seller = einvoice.SellerDtls -%} | ||||
| 		<div class="col-xs-6 column-break"> | ||||
| 			<h5 style="margin-bottom: 5px;">Seller</h5> | ||||
| 			<p>{{ seller.Gstin }}</p> | ||||
| 			<p>{{ seller.LglNm }}</p> | ||||
| 			<p>{{ seller.Addr1 }}</p> | ||||
| 			{%- if seller.Addr2 -%} <p>{{ seller.Addr2 }}</p> {% endif %} | ||||
| 			<p>{{ seller.Loc }}</p> | ||||
| 			<p>{{ frappe.db.get_value("Address", doc.company_address, "gst_state") }} - {{ seller.Pin }}</p> | ||||
| 
 | ||||
| 			{%- if einvoice.ShipDtls -%} | ||||
| 				{%- set shipping = einvoice.ShipDtls -%} | ||||
| 				<h5 style="margin-bottom: 5px;">Shipping</h5> | ||||
| 				<p>{{ shipping.Gstin }}</p> | ||||
| 				<p>{{ shipping.LglNm }}</p> | ||||
| 				<p>{{ shipping.Addr1 }}</p> | ||||
| 				{%- if shipping.Addr2 -%} <p>{{ shipping.Addr2 }}</p> {% endif %} | ||||
| 				<p>{{ shipping.Loc }}</p> | ||||
| 				<p>{{ frappe.db.get_value("Address", doc.shipping_address_name, "gst_state") }} - {{ shipping.Pin }}</p> | ||||
| 			{% endif %} | ||||
| 		</div> | ||||
| 		{%- set buyer = einvoice.BuyerDtls -%} | ||||
| 		<div class="col-xs-6 column-break"> | ||||
| 			<h5 style="margin-bottom: 5px;">Buyer</h5> | ||||
| 			<p>{{ buyer.Gstin }}</p> | ||||
| 			<p>{{ buyer.LglNm }}</p> | ||||
| 			<p>{{ buyer.Addr1 }}</p> | ||||
| 			{%- if buyer.Addr2 -%} <p>{{ buyer.Addr2 }}</p> {% endif %} | ||||
| 			<p>{{ buyer.Loc }}</p> | ||||
| 			<p>{{ frappe.db.get_value("Address", doc.customer_address, "gst_state") }} - {{ buyer.Pin }}</p> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div style="overflow-x: auto;"> | ||||
| 		<h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">3. Item Details</h5> | ||||
| 		<table class="table table-bordered"> | ||||
| 			<thead> | ||||
| 				<tr> | ||||
| 					<th class="text-left" style="width: 3%;">Sr. No.</th> | ||||
| 					<th class="text-left">Item</th> | ||||
| 					<th class="text-left" style="width: 10%;">HSN Code</th> | ||||
| 					<th class="text-left" style="width: 5%;">Qty</th> | ||||
| 					<th class="text-left" style="width: 5%;">UOM</th> | ||||
| 					<th class="text-left">Rate</th> | ||||
| 					<th class="text-left" style="width: 5%;">Discount</th> | ||||
| 					<th class="text-left">Taxable Amount</th> | ||||
| 					<th class="text-left" style="width: 7%;">Tax Rate</th> | ||||
| 					<th class="text-left" style="width: 5%;">Other Charges</th> | ||||
| 					<th class="text-left">Total</th> | ||||
| 				</tr> | ||||
| 			</thead> | ||||
| 			<tbody> | ||||
| 				{% for item in einvoice.ItemList %} | ||||
| 					<tr> | ||||
| 						<td class="text-left" style="width: 3%;">{{ item.SlNo }}</td> | ||||
| 						<td class="text-left">{{ item.PrdDesc }}</td> | ||||
| 						<td class="text-left" style="width: 10%;">{{ item.HsnCd }}</td> | ||||
| 						<td class="text-right" style="width: 5%;">{{ item.Qty }}</td> | ||||
| 						<td class="text-left" style="width: 5%;">{{ item.Unit }}</td> | ||||
| 						<td class="text-right">{{ frappe.utils.fmt_money(item.UnitPrice, None, "INR") }}</td> | ||||
| 						<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(item.Discount, None, "INR") }}</td> | ||||
| 						<td class="text-right">{{ frappe.utils.fmt_money(item.AssAmt, None, "INR") }}</td> | ||||
| 						<td class="text-right" style="width: 7%;">{{ item.GstRt + item.CesRt }} %</td> | ||||
| 						<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(0, None, "INR") }}</td> | ||||
| 						<td class="text-right">{{ frappe.utils.fmt_money(item.TotItemVal, None, "INR") }}</td> | ||||
| 					</tr> | ||||
| 				{% endfor %} | ||||
| 			</tbody> | ||||
| 		</table> | ||||
| 	</div> | ||||
| 	<div style="overflow-x: auto;"> | ||||
| 		<h5 class="font-bold" style="margin-bottom: 0px;">4. Value Details</h5> | ||||
| 		<table class="table table-bordered"> | ||||
| 			<thead> | ||||
| 				<tr> | ||||
| 					<th class="text-left">Taxable Amount</th> | ||||
| 					<th class="text-left">CGST</th> | ||||
| 					<th class="text-left"">SGST</th> | ||||
| 					<th class="text-left">IGST</th> | ||||
| 					<th class="text-left">CESS</th> | ||||
| 					<th class="text-left" style="width: 10%;">State CESS</th> | ||||
| 					<th class="text-left">Discount</th> | ||||
| 					<th class="text-left" style="width: 10%;">Other Charges</th> | ||||
| 					<th class="text-left" style="width: 10%;">Round Off</th> | ||||
| 					<th class="text-left">Total Value</th> | ||||
| 				</tr> | ||||
| 			</thead> | ||||
| 			<tbody> | ||||
| 				{%- set value_details = einvoice.ValDtls -%} | ||||
| 				<tr> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.AssVal, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.CgstVal, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.SgstVal, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.IgstVal, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td> | ||||
| 					<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td> | ||||
| 				</tr> | ||||
| 			</tbody> | ||||
| 		</table> | ||||
| 	</div> | ||||
| </div> | ||||
| @ -1,24 +0,0 @@ | ||||
| { | ||||
|  "align_labels_right": 1, | ||||
|  "creation": "2020-10-10 18:01:21.032914", | ||||
|  "custom_format": 0, | ||||
|  "default_print_language": "en-US", | ||||
|  "disabled": 1, | ||||
|  "doc_type": "Sales Invoice", | ||||
|  "docstatus": 0, | ||||
|  "doctype": "Print Format", | ||||
|  "font": "Default", | ||||
|  "html": "", | ||||
|  "idx": 0, | ||||
|  "line_breaks": 1, | ||||
|  "modified": "2020-10-23 19:54:40.634936", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "GST E-Invoice", | ||||
|  "owner": "Administrator", | ||||
|  "print_format_builder": 0, | ||||
|  "print_format_type": "Jinja", | ||||
|  "raw_printing": 0, | ||||
|  "show_section_headings": 1, | ||||
|  "standard": "Yes" | ||||
| } | ||||
| @ -535,6 +535,8 @@ class ReceivablePayableReport(object): | ||||
| 		if getdate(entry_date) > getdate(self.filters.report_date): | ||||
| 			row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 | ||||
| 
 | ||||
| 		row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5 | ||||
| 
 | ||||
| 	def get_ageing_data(self, entry_date, row): | ||||
| 		# [0-30, 30-60, 60-90, 90-120, 120-above] | ||||
| 		row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 | ||||
|  | ||||
| @ -82,6 +82,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): | ||||
| 			"range3": 0.0, | ||||
| 			"range4": 0.0, | ||||
| 			"range5": 0.0, | ||||
| 			"total_due": 0.0, | ||||
| 			"sales_person": [] | ||||
| 		})) | ||||
| 
 | ||||
| @ -135,3 +136,6 @@ class AccountsReceivableSummary(ReceivablePayableReport): | ||||
| 			"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]), | ||||
| 			"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]): | ||||
| 				self.add_column(label=label, fieldname='range' + str(i+1)) | ||||
| 
 | ||||
| 		# Add column for total due amount | ||||
| 		self.add_column(label="Total Amount Due", fieldname='total_due') | ||||
|  | ||||
| @ -210,10 +210,10 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i | ||||
| 	company_currency = get_company_currency(filters) | ||||
| 
 | ||||
| 	if filters.filter_based_on == 'Fiscal Year': | ||||
| 		start_date = fiscal_year.year_start_date | ||||
| 		start_date = fiscal_year.year_start_date if filters.report != 'Balance Sheet' else None | ||||
| 		end_date = fiscal_year.year_end_date | ||||
| 	else: | ||||
| 		start_date = filters.period_start_date | ||||
| 		start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None | ||||
| 		end_date = filters.period_end_date | ||||
| 
 | ||||
| 	gl_entries_by_account = {} | ||||
|  | ||||
| @ -19,6 +19,7 @@ from erpnext.stock import get_warehouse_account_map | ||||
| 
 | ||||
| class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass | ||||
| class FiscalYearError(frappe.ValidationError): pass | ||||
| class PaymentEntryUnlinkError(frappe.ValidationError): pass | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False): | ||||
| @ -555,10 +556,16 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): | ||||
| 			and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no)) | ||||
| 
 | ||||
| 		for pe in linked_pe: | ||||
| 			pe_doc = frappe.get_doc("Payment Entry", pe) | ||||
| 			pe_doc.set_total_allocated_amount() | ||||
| 			pe_doc.set_unallocated_amount() | ||||
| 			pe_doc.clear_unallocated_reference_document_rows() | ||||
| 			try: | ||||
| 				pe_doc = frappe.get_doc("Payment Entry", pe) | ||||
| 				pe_doc.set_amounts() | ||||
| 				pe_doc.clear_unallocated_reference_document_rows() | ||||
| 				pe_doc.validate_payment_type_with_outstanding() | ||||
| 			except Exception as e: | ||||
| 				msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) | ||||
| 				msg += '<br>' | ||||
| 				msg += _("Please cancel payment entry manually first") | ||||
| 				frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error")) | ||||
| 
 | ||||
| 			frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, | ||||
| 				base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s | ||||
|  | ||||
| @ -565,6 +565,7 @@ | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode", | ||||
|    "show_days": 1, | ||||
|    "show_seconds": 1 | ||||
|   }, | ||||
| @ -1378,7 +1379,7 @@ | ||||
|  "idx": 105, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-05-30 15:17:53.663648", | ||||
|  "modified": "2021-08-17 20:16:12.737743", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Buying", | ||||
|  "name": "Purchase Order", | ||||
|  | ||||
| @ -135,14 +135,9 @@ class AccountsController(TransactionBase): | ||||
| 
 | ||||
| 		validate_regional(self) | ||||
| 
 | ||||
| 		validate_einvoice_fields(self) | ||||
| 
 | ||||
| 		if self.doctype != 'Material Request': | ||||
| 			apply_pricing_rule_on_transaction(self) | ||||
| 
 | ||||
| 	def before_cancel(self): | ||||
| 		validate_einvoice_fields(self) | ||||
| 
 | ||||
| 	def on_trash(self): | ||||
| 		# delete sl and gl entries on deletion of transaction | ||||
| 		if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'): | ||||
| @ -1975,7 +1970,3 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil | ||||
| @erpnext.allow_regional | ||||
| def validate_regional(doc): | ||||
| 	pass | ||||
| 
 | ||||
| @erpnext.allow_regional | ||||
| def validate_einvoice_fields(doc): | ||||
| 	pass | ||||
|  | ||||
| @ -595,7 +595,8 @@ class calculate_taxes_and_totals(object): | ||||
| 				self.doc.precision("outstanding_amount")) | ||||
| 
 | ||||
| 			if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'): | ||||
| 			 	self.update_paid_amount_for_return(total_amount_to_pay) | ||||
| 				self.set_total_amount_to_default_mop(total_amount_to_pay) | ||||
| 				self.calculate_paid_amount() | ||||
| 
 | ||||
| 	def calculate_paid_amount(self): | ||||
| 
 | ||||
| @ -675,7 +676,7 @@ class calculate_taxes_and_totals(object): | ||||
| 	def set_item_wise_tax_breakup(self): | ||||
| 		self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) | ||||
| 
 | ||||
| 	def update_paid_amount_for_return(self, total_amount_to_pay): | ||||
| 	def set_total_amount_to_default_mop(self, total_amount_to_pay): | ||||
| 		default_mode_of_payment = frappe.db.get_value('POS Payment Method', | ||||
| 			{'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) | ||||
| 
 | ||||
| @ -687,8 +688,6 @@ class calculate_taxes_and_totals(object): | ||||
| 				'default': 1 | ||||
| 			})	 | ||||
| 
 | ||||
| 		self.calculate_paid_amount() | ||||
| 
 | ||||
| def get_itemised_tax_breakup_html(doc): | ||||
| 	if not doc.taxes: | ||||
| 		return | ||||
|  | ||||
| @ -251,7 +251,7 @@ doc_events = { | ||||
| 			"erpnext.support.doctype.issue.issue.set_first_response_time" | ||||
| 		] | ||||
| 	}, | ||||
| 	("Sales Taxes and Charges Template", 'Price List'): { | ||||
| 	"Sales Taxes and Charges Template": { | ||||
| 		"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings" | ||||
| 	}, | ||||
| 	"Website Settings": { | ||||
| @ -308,6 +308,9 @@ doc_events = { | ||||
| 	}, | ||||
| 	('Quotation', 'Sales Order', 'Sales Invoice'): { | ||||
| 		'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"] | ||||
| 	}, | ||||
| 	"Company": { | ||||
| 		"on_trash": "erpnext.regional.india.utils.delete_gst_settings_for_company" | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -436,7 +439,6 @@ regional_overrides = { | ||||
| 		'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts', | ||||
| 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', | ||||
| 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', | ||||
| 		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields', | ||||
| 		'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount', | ||||
| 		'erpnext.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code' | ||||
| 	}, | ||||
|  | ||||
| @ -235,7 +235,7 @@ frappe.ui.form.on("BOM", { | ||||
| 						reqd: 1, | ||||
| 					}, | ||||
| 					{ | ||||
| 						fieldname: "varint_item_code", | ||||
| 						fieldname: "variant_item_code", | ||||
| 						options: "Item", | ||||
| 						label: __("Variant Item"), | ||||
| 						fieldtype: "Link", | ||||
| @ -287,7 +287,7 @@ frappe.ui.form.on("BOM", { | ||||
| 			let variant_items = data.items || []; | ||||
| 
 | ||||
| 			variant_items.forEach(d => { | ||||
| 				if (!d.varint_item_code) { | ||||
| 				if (!d.variant_item_code) { | ||||
| 					frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); | ||||
| 				} | ||||
| 			}) | ||||
| @ -299,7 +299,7 @@ frappe.ui.form.on("BOM", { | ||||
| 		has_template_rm.forEach(d => { | ||||
| 			dialog.fields_dict.items.df.data.push({ | ||||
| 				"item_code": d.item_code, | ||||
| 				"varint_item_code": "", | ||||
| 				"variant_item_code": "", | ||||
| 				"qty": d.qty, | ||||
| 				"source_warehouse": d.source_warehouse, | ||||
| 				"operation": d.operation | ||||
|  | ||||
| @ -297,7 +297,7 @@ class ProductionPlan(Document): | ||||
| 
 | ||||
| 		if self.total_produced_qty > 0: | ||||
| 			self.status = "In Process" | ||||
| 			if self.total_produced_qty == self.total_planned_qty: | ||||
| 			if self.total_produced_qty >= self.total_planned_qty: | ||||
| 				self.status = "Completed" | ||||
| 
 | ||||
| 		if self.status != 'Completed': | ||||
| @ -346,6 +346,7 @@ class ProductionPlan(Document): | ||||
| 				"production_plan"       : self.name, | ||||
| 				"production_plan_item"  : d.name, | ||||
| 				"product_bundle_item"	: d.product_bundle_item, | ||||
| 				"planned_start_date"    : d.planned_start_date, | ||||
| 				"make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0) | ||||
| 			} | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ from __future__ import unicode_literals | ||||
| 
 | ||||
| import frappe | ||||
| import unittest | ||||
| from frappe.utils import nowdate, now_datetime, flt | ||||
| from frappe.utils import nowdate, now_datetime, flt, add_to_date | ||||
| from erpnext.stock.doctype.item.test_item import create_item | ||||
| from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders | ||||
| from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation | ||||
| @ -61,6 +61,21 @@ class TestProductionPlan(unittest.TestCase): | ||||
| 		pln = frappe.get_doc('Production Plan', pln.name) | ||||
| 		pln.cancel() | ||||
| 
 | ||||
| 	def test_production_plan_start_date(self): | ||||
| 		planned_date = add_to_date(date=None, days=3) | ||||
| 		plan = create_production_plan(item_code='Test Production Item 1', planned_start_date=planned_date) | ||||
| 		plan.make_work_order() | ||||
| 
 | ||||
| 		work_orders = frappe.get_all('Work Order', fields = ['name', 'planned_start_date'], | ||||
| 			filters = {'production_plan': plan.name}) | ||||
| 
 | ||||
| 		self.assertEqual(work_orders[0].planned_start_date, planned_date) | ||||
| 
 | ||||
| 		for wo in work_orders: | ||||
| 			frappe.delete_doc('Work Order', wo.name) | ||||
| 
 | ||||
| 		frappe.get_doc('Production Plan', plan.name).cancel() | ||||
| 
 | ||||
| 	def test_production_plan_for_existing_ordered_qty(self): | ||||
| 		sr1 = create_stock_reconciliation(item_code="Raw Material Item 1", | ||||
| 			target="_Test Warehouse - _TC", qty=1, rate=110) | ||||
|  | ||||
| @ -602,7 +602,7 @@ class WorkOrder(Document): | ||||
| 
 | ||||
| 		if self.docstatus==1: | ||||
| 			# calculate transferred qty based on submitted stock entries | ||||
| 			self.update_transaferred_qty_for_required_items() | ||||
| 			self.update_transferred_qty_for_required_items() | ||||
| 
 | ||||
| 			# update in bin | ||||
| 			self.update_reserved_qty_for_production() | ||||
| @ -671,7 +671,7 @@ class WorkOrder(Document): | ||||
| 
 | ||||
| 			self.set_available_qty() | ||||
| 
 | ||||
| 	def update_transaferred_qty_for_required_items(self): | ||||
| 	def update_transferred_qty_for_required_items(self): | ||||
| 		'''update transferred qty from submitted stock entries for that item against | ||||
| 			the work order''' | ||||
| 
 | ||||
| @ -838,7 +838,7 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): | ||||
| 
 | ||||
| 	for item in variant_items: | ||||
| 		args = frappe._dict({ | ||||
| 			"item_code": item.get("varint_item_code"), | ||||
| 			"item_code": item.get("variant_item_code"), | ||||
| 			"required_qty": item.get("qty"), | ||||
| 			"qty": item.get("qty"), # for bom | ||||
| 			"source_warehouse": item.get("source_warehouse"), | ||||
| @ -859,7 +859,7 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): | ||||
| 		}, bom_doc) | ||||
| 
 | ||||
| 		if not args.source_warehouse: | ||||
| 			args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"), | ||||
| 			args["source_warehouse"] = get_item_defaults(item.get("variant_item_code"), | ||||
| 				wo_doc.company).default_warehouse | ||||
| 
 | ||||
| 		args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) | ||||
|  | ||||
| @ -214,7 +214,6 @@ erpnext.patches.v13_0.delete_old_sales_reports | ||||
| execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") | ||||
| erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020 | ||||
| erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 | ||||
| execute:frappe.reload_doc("regional", "doctype", "e_invoice_settings") | ||||
| erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 | ||||
| erpnext.patches.v13_0.loyalty_points_entry_for_pos_invoice #22-07-2020 | ||||
| erpnext.patches.v12_0.add_taxjar_integration_field | ||||
| @ -237,7 +236,6 @@ erpnext.patches.v13_0.set_app_name | ||||
| erpnext.patches.v13_0.print_uom_after_quantity_patch | ||||
| erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account | ||||
| erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail | ||||
| erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02 | ||||
| erpnext.patches.v13_0.updates_for_multi_currency_payroll | ||||
| erpnext.patches.v13_0.update_reason_for_resignation_in_employee | ||||
| execute:frappe.delete_doc("Report", "Quoted Item Comparison") | ||||
| @ -259,14 +257,11 @@ erpnext.patches.v12_0.add_state_code_for_ladakh | ||||
| erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl | ||||
| erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes | ||||
| erpnext.patches.v12_0.update_vehicle_no_reqd_condition | ||||
| erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17 | ||||
| erpnext.patches.v12_0.add_einvoice_summary_report_permissions | ||||
| erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation | ||||
| erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings | ||||
| erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae | ||||
| erpnext.patches.v13_0.setup_uae_vat_fields | ||||
| execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext') | ||||
| erpnext.patches.v12_0.add_company_link_to_einvoice_settings | ||||
| erpnext.patches.v13_0.rename_discharge_date_in_ip_record | ||||
| erpnext.patches.v12_0.create_taxable_value_field | ||||
| erpnext.patches.v12_0.add_gst_category_in_delivery_note | ||||
| @ -277,7 +272,6 @@ erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing | ||||
| erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 | ||||
| erpnext.patches.v13_0.update_shipment_status | ||||
| erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting | ||||
| erpnext.patches.v12_0.add_ewaybill_validity_field | ||||
| erpnext.patches.v13_0.germany_make_custom_fields | ||||
| erpnext.patches.v13_0.germany_fill_debtor_creditor_number | ||||
| erpnext.patches.v13_0.set_pos_closing_as_failed | ||||
| @ -294,10 +288,11 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef | ||||
| erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry | ||||
| erpnext.patches.v13_0.update_subscription_status_in_memberships | ||||
| erpnext.patches.v13_0.update_amt_in_work_order_required_items | ||||
| erpnext.patches.v12_0.show_einvoice_irn_cancelled_field | ||||
| erpnext.patches.v13_0.delete_orphaned_tables | ||||
| erpnext.patches.v13_0.update_export_type_for_gst #2021-08-16 | ||||
| erpnext.patches.v13_0.update_tds_check_field #3 | ||||
| erpnext.patches.v13_0.add_custom_field_for_south_africa #2 | ||||
| erpnext.patches.v13_0.update_recipient_email_digest | ||||
| erpnext.patches.v13_0.shopify_deprecation_warning | ||||
| erpnext.patches.v13_0.einvoicing_deprecation_warning | ||||
| erpnext.patches.v14_0.delete_einvoicing_doctypes | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company or not frappe.db.count('E Invoice User'): | ||||
| 		return | ||||
| 
 | ||||
| 	frappe.reload_doc("regional", "doctype", "e_invoice_user") | ||||
| 	for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']): | ||||
| 		company_name = frappe.db.sql(""" | ||||
| 			select dl.link_name from `tabAddress` a, `tabDynamic Link` dl | ||||
| 			where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company' | ||||
| 		""", (creds.get('gstin'))) | ||||
| 		if company_name and len(company_name) > 0: | ||||
| 			frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0]) | ||||
| @ -1,69 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import json | ||||
| import frappe | ||||
| from frappe.custom.doctype.custom_field.custom_field import create_custom_fields | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company: | ||||
| 		return | ||||
| 
 | ||||
| 	# move hidden einvoice fields to a different section | ||||
| 	custom_fields = { | ||||
| 		'Sales Invoice': [ | ||||
| 			dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type', | ||||
| 			print_hide=1, hidden=1), | ||||
| 
 | ||||
| 			dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section', | ||||
| 				no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 			dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 			dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', | ||||
| 				no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 			dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date', | ||||
| 				no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice', | ||||
| 				no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code', | ||||
| 				no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image', | ||||
| 				options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON', | ||||
| 				hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1) | ||||
| 		] | ||||
| 	} | ||||
| 	create_custom_fields(custom_fields, update=True) | ||||
| 
 | ||||
| 	if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'): | ||||
| 		frappe.db.sql(''' | ||||
| 			UPDATE `tabSales Invoice` SET einvoice_status = 'Pending' | ||||
| 			WHERE | ||||
| 				posting_date >= '2021-04-01' | ||||
| 				AND ifnull(irn, '') = '' | ||||
| 				AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '') | ||||
| 				AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export') | ||||
| 		''') | ||||
| 
 | ||||
| 		# set appropriate statuses | ||||
| 		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated' | ||||
| 			WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''') | ||||
| 
 | ||||
| 		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled' | ||||
| 			WHERE ifnull(irn_cancelled, 0) = 1''') | ||||
| 
 | ||||
| 	# set correct acknowledgement in e-invoices | ||||
| 	einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice']) | ||||
| 
 | ||||
| 	if einvoices: | ||||
| 		for inv in einvoices: | ||||
| 			signed_einvoice = inv.get('signed_einvoice') | ||||
| 			if signed_einvoice: | ||||
| 				signed_einvoice = json.loads(signed_einvoice) | ||||
| 				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False) | ||||
| 				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False) | ||||
| @ -1,18 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company: | ||||
| 		return | ||||
| 
 | ||||
| 	if frappe.db.exists('Report', 'E-Invoice Summary') and \ | ||||
| 		not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')): | ||||
| 		frappe.get_doc(dict( | ||||
| 			doctype='Custom Role', | ||||
| 			report='E-Invoice Summary', | ||||
| 			roles= [ | ||||
| 				dict(role='Accounts User'), | ||||
| 				dict(role='Accounts Manager') | ||||
| 			] | ||||
| 		)).insert() | ||||
| @ -1,16 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe.custom.doctype.custom_field.custom_field import create_custom_fields | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company: | ||||
| 		return | ||||
| 
 | ||||
| 	custom_fields = { | ||||
| 		'Sales Invoice': [ | ||||
| 			dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1, | ||||
| 				depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill') | ||||
| 		] | ||||
| 	} | ||||
| 	create_custom_fields(custom_fields, update=True) | ||||
| @ -1,56 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe.custom.doctype.custom_field.custom_field import create_custom_fields | ||||
| from erpnext.regional.india.setup import add_permissions, add_print_formats | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company: | ||||
| 		return | ||||
| 
 | ||||
| 	frappe.reload_doc("custom", "doctype", "custom_field") | ||||
| 	frappe.reload_doc("regional", "doctype", "e_invoice_settings") | ||||
| 	custom_fields = { | ||||
| 		'Sales Invoice': [ | ||||
| 			dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1, | ||||
| 				depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'), | ||||
| 
 | ||||
| 			dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 			dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 			dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, | ||||
| 				depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), | ||||
| 
 | ||||
| 			dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1, | ||||
| 				depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), | ||||
| 
 | ||||
| 			dict(fieldname='signed_einvoice', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='signed_qr_code', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 			dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, no_copy=1, print_hide=1, read_only=1) | ||||
| 		] | ||||
| 	} | ||||
| 	create_custom_fields(custom_fields, update=True) | ||||
| 	add_permissions() | ||||
| 	add_print_formats() | ||||
| 
 | ||||
| 	einvoice_cond = 'in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category)' | ||||
| 	t = { | ||||
| 		'mode_of_transport': [{'default': None}], | ||||
| 		'distance': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.transporter'}], | ||||
| 		'gst_vehicle_type': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}], | ||||
| 		'lr_date': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}], | ||||
| 		'lr_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}], | ||||
| 		'vehicle_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}], | ||||
| 		'ewaybill': [ | ||||
| 			{'read_only_depends_on': 'eval:doc.irn && doc.ewaybill'}, | ||||
| 			{'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)'} | ||||
| 		] | ||||
| 	} | ||||
| 
 | ||||
| 	for field, conditions in t.items(): | ||||
| 		for c in conditions: | ||||
| 			[(prop, value)] = c.items() | ||||
| 			frappe.db.set_value('Custom Field', { 'fieldname': field }, prop, value) | ||||
| @ -1,12 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| 
 | ||||
| def execute(): | ||||
| 	company = frappe.get_all('Company', filters = {'country': 'India'}) | ||||
| 	if not company: | ||||
| 		return | ||||
| 
 | ||||
| 	irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'}) | ||||
| 	if irn_cancelled_field: | ||||
| 		frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn') | ||||
| 		frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0) | ||||
							
								
								
									
										9
									
								
								erpnext/patches/v13_0/einvoicing_deprecation_warning.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								erpnext/patches/v13_0/einvoicing_deprecation_warning.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| import click | ||||
| 
 | ||||
| 
 | ||||
| def execute(): | ||||
| 	click.secho( | ||||
| 		"Indian E-Invoicing integration is moved to a separate app and will be removed from ERPNext in version-14.\n" | ||||
| 		"Please install the app to continue using the integration: https://github.com/frappe/erpnext_gst_compliance", | ||||
| 		fg="yellow", | ||||
| 	) | ||||
							
								
								
									
										9
									
								
								erpnext/patches/v14_0/delete_einvoicing_doctypes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								erpnext/patches/v14_0/delete_einvoicing_doctypes.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| import frappe | ||||
| 
 | ||||
| def execute(): | ||||
| 	frappe.delete_doc('DocType', 'E Invoice Settings', ignore_missing=True) | ||||
| 	frappe.delete_doc('DocType', 'E Invoice User', ignore_missing=True) | ||||
| 	frappe.delete_doc('Report', 'E-Invoice Summary', ignore_missing=True) | ||||
| 	frappe.delete_doc('Print Format', 'GST E-Invoice', ignore_missing=True) | ||||
| 	frappe.delete_doc('Custom Field', 'Sales Invoice-eway_bill_cancelled', ignore_missing=True) | ||||
| 	frappe.delete_doc('Custom Field', 'Sales Invoice-irn_cancelled', ignore_missing=True) | ||||
| @ -4,18 +4,11 @@ | ||||
| frappe.ui.form.on('Salary Component', { | ||||
| 	setup: function(frm) { | ||||
| 		frm.set_query("account", "accounts", function(doc, cdt, cdn) { | ||||
| 			let d = frappe.get_doc(cdt, cdn); | ||||
| 
 | ||||
| 			let root_type = "Liability"; | ||||
| 			if (frm.doc.type == "Deduction") { | ||||
| 				root_type = "Expense"; | ||||
| 			} | ||||
| 
 | ||||
| 			var d = locals[cdt][cdn]; | ||||
| 			return { | ||||
| 				filters: { | ||||
| 					"is_group": 0, | ||||
| 					"company": d.company, | ||||
| 					"root_type": root_type | ||||
| 					"company": d.company | ||||
| 				} | ||||
| 			}; | ||||
| 		}); | ||||
|  | ||||
| @ -754,8 +754,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { | ||||
| 			} | ||||
| 		}); | ||||
| 		this.frm.refresh_fields(); | ||||
| 
 | ||||
| 		this.calculate_paid_amount(); | ||||
| 	} | ||||
| 
 | ||||
| 	set_default_payment(total_amount_to_pay, update_paid_amount) { | ||||
|  | ||||
| @ -342,30 +342,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe | ||||
| 		this.set_dynamic_labels(); | ||||
| 		this.setup_sms(); | ||||
| 		this.setup_quality_inspection(); | ||||
| 		let scan_barcode_field = this.frm.get_field('scan_barcode'); | ||||
| 		if (scan_barcode_field && scan_barcode_field.get_value()) { | ||||
| 			scan_barcode_field.set_value(""); | ||||
| 			scan_barcode_field.set_new_description(""); | ||||
| 
 | ||||
| 			if (frappe.is_mobile()) { | ||||
| 				if (scan_barcode_field.$input_wrapper.find('.input-group').length) return; | ||||
| 
 | ||||
| 				let $input_group = $('<div class="input-group">'); | ||||
| 				scan_barcode_field.$input_wrapper.find('.control-input').append($input_group); | ||||
| 				$input_group.append(scan_barcode_field.$input); | ||||
| 				$(`<span class="input-group-btn" style="vertical-align: top">
 | ||||
| 						<button class="btn btn-default border" type="button"> | ||||
| 							<i class="fa fa-camera text-muted"></i> | ||||
| 						</button> | ||||
| 					</span>`) | ||||
| 					.on('click', '.btn', () => { | ||||
| 						frappe.barcode.scan_barcode().then(barcode => { | ||||
| 							scan_barcode_field.set_value(barcode); | ||||
| 						}); | ||||
| 					}) | ||||
| 					.appendTo($input_group); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	scan_barcode() { | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
 | ||||
| // For license information, please see license.txt
 | ||||
| 
 | ||||
| frappe.ui.form.on('E Invoice Request Log', { | ||||
| 	// refresh: function(frm) {
 | ||||
| 
 | ||||
| 	// }
 | ||||
| }); | ||||
| @ -1,102 +0,0 @@ | ||||
| { | ||||
|  "actions": [], | ||||
|  "autoname": "EINV-REQ-.#####", | ||||
|  "creation": "2020-12-08 12:54:08.175992", | ||||
|  "doctype": "DocType", | ||||
|  "editable_grid": 1, | ||||
|  "engine": "InnoDB", | ||||
|  "field_order": [ | ||||
|   "user", | ||||
|   "url", | ||||
|   "headers", | ||||
|   "response", | ||||
|   "column_break_7", | ||||
|   "timestamp", | ||||
|   "reference_invoice", | ||||
|   "data" | ||||
|  ], | ||||
|  "fields": [ | ||||
|   { | ||||
|    "fieldname": "user", | ||||
|    "fieldtype": "Link", | ||||
|    "label": "User", | ||||
|    "options": "User" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "reference_invoice", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Reference Invoice" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "headers", | ||||
|    "fieldtype": "Code", | ||||
|    "label": "Headers", | ||||
|    "options": "JSON" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "data", | ||||
|    "fieldtype": "Code", | ||||
|    "label": "Data", | ||||
|    "options": "JSON" | ||||
|   }, | ||||
|   { | ||||
|    "default": "Now", | ||||
|    "fieldname": "timestamp", | ||||
|    "fieldtype": "Datetime", | ||||
|    "label": "Timestamp" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "response", | ||||
|    "fieldtype": "Code", | ||||
|    "label": "Response", | ||||
|    "options": "JSON" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "url", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "URL" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "column_break_7", | ||||
|    "fieldtype": "Column Break" | ||||
|   } | ||||
|  ], | ||||
|  "index_web_pages_for_search": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-01-13 12:06:57.253111", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Regional", | ||||
|  "name": "E Invoice Request Log", | ||||
|  "owner": "Administrator", | ||||
|  "permissions": [ | ||||
|   { | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "System Manager", | ||||
|    "share": 1 | ||||
|   }, | ||||
|   { | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "Accounts User", | ||||
|    "share": 1 | ||||
|   }, | ||||
|   { | ||||
|    "email": 1, | ||||
|    "export": 1, | ||||
|    "print": 1, | ||||
|    "read": 1, | ||||
|    "report": 1, | ||||
|    "role": "Accounts Manager", | ||||
|    "share": 1 | ||||
|   } | ||||
|  ], | ||||
|  "sort_field": "modified", | ||||
|  "sort_order": "DESC" | ||||
| } | ||||
| @ -1,10 +0,0 @@ | ||||
| # -*- 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 EInvoiceRequestLog(Document): | ||||
| 	pass | ||||
| @ -1,10 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | ||||
| # See license.txt | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| # import frappe | ||||
| import unittest | ||||
| 
 | ||||
| class TestEInvoiceRequestLog(unittest.TestCase): | ||||
| 	pass | ||||
| @ -1,11 +0,0 @@ | ||||
| // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
 | ||||
| // For license information, please see license.txt
 | ||||
| 
 | ||||
| frappe.ui.form.on('E Invoice Settings', { | ||||
| 	refresh(frm) { | ||||
| 		const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing'; | ||||
| 		frm.dashboard.set_headline( | ||||
| 			__("Read {0} for more information on E Invoicing features.", [`<a href='${docs_link}'>documentation</a>`]) | ||||
| 		); | ||||
| 	} | ||||
| }); | ||||
| @ -1,73 +0,0 @@ | ||||
| { | ||||
|  "actions": [], | ||||
|  "creation": "2020-09-24 16:23:16.235722", | ||||
|  "doctype": "DocType", | ||||
|  "editable_grid": 1, | ||||
|  "engine": "InnoDB", | ||||
|  "field_order": [ | ||||
|   "enable", | ||||
|   "section_break_2", | ||||
|   "sandbox_mode", | ||||
|   "applicable_from", | ||||
|   "credentials", | ||||
|   "auth_token", | ||||
|   "token_expiry" | ||||
|  ], | ||||
|  "fields": [ | ||||
|   { | ||||
|    "default": "0", | ||||
|    "fieldname": "enable", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Enable" | ||||
|   }, | ||||
|   { | ||||
|    "depends_on": "enable", | ||||
|    "fieldname": "section_break_2", | ||||
|    "fieldtype": "Section Break" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "auth_token", | ||||
|    "fieldtype": "Data", | ||||
|    "hidden": 1, | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "token_expiry", | ||||
|    "fieldtype": "Datetime", | ||||
|    "hidden": 1, | ||||
|    "read_only": 1 | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "credentials", | ||||
|    "fieldtype": "Table", | ||||
|    "label": "Credentials", | ||||
|    "mandatory_depends_on": "enable", | ||||
|    "options": "E Invoice User" | ||||
|   }, | ||||
|   { | ||||
|    "default": "0", | ||||
|    "fieldname": "sandbox_mode", | ||||
|    "fieldtype": "Check", | ||||
|    "label": "Sandbox Mode" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "applicable_from", | ||||
|    "fieldtype": "Date", | ||||
|    "in_list_view": 1, | ||||
|    "label": "Applicable From", | ||||
|    "reqd": 1 | ||||
|   } | ||||
|  ], | ||||
|  "index_web_pages_for_search": 1, | ||||
|  "issingle": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-03-30 12:26:25.538294", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Regional", | ||||
|  "name": "E Invoice Settings", | ||||
|  "owner": "Administrator", | ||||
|  "permissions": [], | ||||
|  "sort_field": "modified", | ||||
|  "sort_order": "DESC", | ||||
|  "track_changes": 1 | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| # -*- 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): | ||||
| 	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.')) | ||||
| @ -1,10 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors | ||||
| # See license.txt | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| # import frappe | ||||
| import unittest | ||||
| 
 | ||||
| class TestEInvoiceSettings(unittest.TestCase): | ||||
| 	pass | ||||
| @ -1,57 +0,0 @@ | ||||
| { | ||||
|  "actions": [], | ||||
|  "creation": "2020-12-22 15:02:46.229474", | ||||
|  "doctype": "DocType", | ||||
|  "editable_grid": 1, | ||||
|  "engine": "InnoDB", | ||||
|  "field_order": [ | ||||
|   "company", | ||||
|   "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 | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "company", | ||||
|    "fieldtype": "Link", | ||||
|    "in_list_view": 1, | ||||
|    "label": "Company", | ||||
|    "options": "Company", | ||||
|    "reqd": 1 | ||||
|   } | ||||
|  ], | ||||
|  "index_web_pages_for_search": 1, | ||||
|  "istable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-03-22 12:16:56.365616", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Regional", | ||||
|  "name": "E Invoice User", | ||||
|  "owner": "Administrator", | ||||
|  "permissions": [], | ||||
|  "quick_entry": 1, | ||||
|  "sort_field": "modified", | ||||
|  "sort_order": "DESC", | ||||
|  "track_changes": 1 | ||||
| } | ||||
| @ -1,10 +0,0 @@ | ||||
| # -*- 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 | ||||
| @ -1,31 +0,0 @@ | ||||
| {{ | ||||
|     "SlNo": "{item.sr_no}", | ||||
|     "PrdDesc": "{item.description}", | ||||
|     "IsServc": "{item.is_service_item}", | ||||
|     "HsnCd": "{item.gst_hsn_code}", | ||||
|     "Barcde": "{item.barcode}", | ||||
|     "Unit": "{item.uom}", | ||||
|     "Qty": "{item.qty}", | ||||
|     "FreeQty": "{item.free_qty}", | ||||
|     "UnitPrice": "{item.unit_rate}", | ||||
|     "TotAmt": "{item.gross_amount}", | ||||
|     "Discount": "{item.discount_amount}", | ||||
|     "AssAmt": "{item.taxable_value}", | ||||
|     "PrdSlNo": "{item.serial_no}", | ||||
|     "GstRt": "{item.tax_rate}", | ||||
|     "IgstAmt": "{item.igst_amount}", | ||||
|     "CgstAmt": "{item.cgst_amount}", | ||||
|     "SgstAmt": "{item.sgst_amount}", | ||||
|     "CesRt": "{item.cess_rate}", | ||||
|     "CesAmt": "{item.cess_amount}", | ||||
|     "CesNonAdvlAmt": "{item.cess_nadv_amount}", | ||||
|     "StateCesRt": "{item.state_cess_rate}", | ||||
|     "StateCesAmt": "{item.state_cess_amount}", | ||||
|     "StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}", | ||||
|     "OthChrg": "{item.other_charges}", | ||||
|     "TotItemVal": "{item.total_value}", | ||||
|     "BchDtls": {{ | ||||
|         "Nm": "{item.batch_no}", | ||||
|         "ExpDt": "{item.batch_expiry_date}" | ||||
|     }} | ||||
| }} | ||||
| @ -1,110 +0,0 @@ | ||||
| {{ | ||||
|     "Version": "1.1", | ||||
|     "TranDtls": {{ | ||||
|         "TaxSch": "{transaction_details.tax_scheme}", | ||||
|         "SupTyp": "{transaction_details.supply_type}", | ||||
|         "RegRev": "{transaction_details.reverse_charge}", | ||||
|         "EcmGstin": "{transaction_details.ecom_gstin}", | ||||
|         "IgstOnIntra": "{transaction_details.igst_on_intra}" | ||||
|     }}, | ||||
|     "DocDtls": {{ | ||||
|         "Typ": "{doc_details.invoice_type}", | ||||
|         "No": "{doc_details.invoice_name}", | ||||
|         "Dt": "{doc_details.invoice_date}" | ||||
|     }}, | ||||
|     "SellerDtls": {{ | ||||
|         "Gstin": "{seller_details.gstin}", | ||||
|         "LglNm": "{seller_details.legal_name}", | ||||
|         "TrdNm": "{seller_details.trade_name}", | ||||
|         "Loc": "{seller_details.location}", | ||||
|         "Pin": "{seller_details.pincode}", | ||||
|         "Stcd": "{seller_details.state_code}", | ||||
|         "Addr1": "{seller_details.address_line1}", | ||||
|         "Addr2": "{seller_details.address_line2}", | ||||
|         "Ph": "{seller_details.phone}", | ||||
|         "Em": "{seller_details.email}" | ||||
|     }}, | ||||
|     "BuyerDtls": {{ | ||||
|         "Gstin": "{buyer_details.gstin}", | ||||
|         "LglNm": "{buyer_details.legal_name}", | ||||
|         "TrdNm": "{buyer_details.trade_name}", | ||||
|         "Addr1": "{buyer_details.address_line1}", | ||||
|         "Addr2": "{buyer_details.address_line2}", | ||||
|         "Loc": "{buyer_details.location}", | ||||
|         "Pin": "{buyer_details.pincode}", | ||||
|         "Stcd": "{buyer_details.state_code}", | ||||
|         "Ph": "{buyer_details.phone}", | ||||
|         "Em": "{buyer_details.email}", | ||||
|         "Pos": "{buyer_details.place_of_supply}" | ||||
|     }}, | ||||
|     "DispDtls": {{ | ||||
|         "Nm": "{dispatch_details.company_name}", | ||||
|         "Addr1": "{dispatch_details.address_line1}", | ||||
|         "Addr2": "{dispatch_details.address_line2}", | ||||
|         "Loc": "{dispatch_details.location}", | ||||
|         "Pin": "{dispatch_details.pincode}", | ||||
|         "Stcd": "{dispatch_details.state_code}" | ||||
|     }}, | ||||
|     "ShipDtls": {{ | ||||
|         "Gstin": "{shipping_details.gstin}", | ||||
|         "LglNm": "{shipping_details.legal_name}", | ||||
|         "TrdNm": "{shipping_details.trader_name}", | ||||
|         "Addr1": "{shipping_details.address_line1}", | ||||
|         "Addr2": "{shipping_details.address_line2}", | ||||
|         "Loc": "{shipping_details.location}", | ||||
|         "Pin": "{shipping_details.pincode}", | ||||
|         "Stcd": "{shipping_details.state_code}" | ||||
|     }}, | ||||
|     "ItemList": [ | ||||
|         {item_list} | ||||
|     ], | ||||
|     "ValDtls": {{ | ||||
|         "AssVal": "{invoice_value_details.base_total}", | ||||
|         "CgstVal": "{invoice_value_details.total_cgst_amt}", | ||||
|         "SgstVal": "{invoice_value_details.total_sgst_amt}", | ||||
|         "IgstVal": "{invoice_value_details.total_igst_amt}", | ||||
|         "CesVal": "{invoice_value_details.total_cess_amt}", | ||||
|         "Discount": "{invoice_value_details.invoice_discount_amt}", | ||||
|         "RndOffAmt": "{invoice_value_details.round_off}", | ||||
|         "OthChrg": "{invoice_value_details.total_other_charges}", | ||||
|         "TotInvVal": "{invoice_value_details.base_grand_total}", | ||||
|         "TotInvValFc": "{invoice_value_details.grand_total}" | ||||
|     }}, | ||||
|     "PayDtls": {{ | ||||
|         "Nm": "{payment_details.payee_name}", | ||||
|         "AccDet": "{payment_details.account_no}", | ||||
|         "Mode": "{payment_details.mode_of_payment}", | ||||
|         "FinInsBr": "{payment_details.ifsc_code}", | ||||
|         "PayTerm": "{payment_details.terms}", | ||||
|         "PaidAmt": "{payment_details.paid_amount}", | ||||
|         "PaymtDue": "{payment_details.outstanding_amount}" | ||||
|     }}, | ||||
|     "RefDtls": {{ | ||||
|         "DocPerdDtls": {{ | ||||
|             "InvStDt": "{period_details.start_date}", | ||||
|             "InvEndDt": "{period_details.end_date}" | ||||
|         }}, | ||||
|         "PrecDocDtls": [{{ | ||||
|             "InvNo": "{prev_doc_details.invoice_name}", | ||||
|             "InvDt": "{prev_doc_details.invoice_date}" | ||||
|         }}] | ||||
|     }}, | ||||
|     "ExpDtls": {{ | ||||
|         "ShipBNo": "{export_details.bill_no}", | ||||
|         "ShipBDt": "{export_details.bill_date}", | ||||
|         "Port": "{export_details.port}", | ||||
|         "ForCur": "{export_details.foreign_curr_code}", | ||||
|         "CntCode": "{export_details.country_code}", | ||||
|         "ExpDuty": "{export_details.export_duty}" | ||||
|     }}, | ||||
|     "EwbDtls": {{ | ||||
|         "TransId": "{eway_bill_details.gstin}", | ||||
|         "TransName": "{eway_bill_details.name}", | ||||
|         "TransMode": "{eway_bill_details.mode_of_transport}", | ||||
|         "Distance": "{eway_bill_details.distance}", | ||||
|         "TransDocNo": "{eway_bill_details.document_name}", | ||||
|         "TransDocDt": "{eway_bill_details.document_date}", | ||||
|         "VehNo": "{eway_bill_details.vehicle_no}", | ||||
|         "VehType": "{eway_bill_details.vehicle_type}" | ||||
|     }} | ||||
| }} | ||||
| @ -1,957 +0,0 @@ | ||||
| { | ||||
|   "Version": { | ||||
|     "type": "string", | ||||
|     "minLength": 1, | ||||
|     "maxLength": 6, | ||||
|     "description": "Version of the schema" | ||||
|   }, | ||||
|   "Irn": { | ||||
|     "type": "string", | ||||
|     "minLength": 64, | ||||
|     "maxLength": 64, | ||||
|     "description": "Invoice Reference Number" | ||||
|   }, | ||||
|   "TranDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "TaxSch": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 10, | ||||
|         "enum": ["GST"], | ||||
|         "description": "GST- Goods and Services Tax Scheme" | ||||
|       }, | ||||
|       "SupTyp": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 10, | ||||
|         "enum": ["B2B", "SEZWP", "SEZWOP", "EXPWP", "EXPWOP", "DEXP"], | ||||
|         "description": "Type of Supply: B2B-Business to Business, SEZWP - SEZ with payment, SEZWOP - SEZ without payment, EXPWP - Export with Payment, EXPWOP - Export without payment,DEXP - Deemed Export" | ||||
|       }, | ||||
|       "RegRev": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 1, | ||||
|         "enum": ["Y", "N"], | ||||
|         "description": "Y- whether the tax liability is payable under reverse charge" | ||||
|       }, | ||||
|       "EcmGstin": { | ||||
|         "type": "string", | ||||
|         "minLength": 15, | ||||
|         "maxLength": 15, | ||||
|         "pattern": "([0-9]{2}[0-9A-Z]{13})", | ||||
|         "description": "E-Commerce GSTIN", | ||||
|         "validationMsg": "E-Commerce GSTIN is invalid" | ||||
|       }, | ||||
|       "IgstOnIntra": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 1, | ||||
|         "enum": ["Y", "N"], | ||||
|         "description": "Y- indicates the supply is intra state but chargeable to IGST" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["TaxSch", "SupTyp"] | ||||
|   }, | ||||
|   "DocDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Typ": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 3, | ||||
|         "enum": ["INV", "CRN", "DBN"], | ||||
|         "description": "Document Type" | ||||
|       }, | ||||
|       "No": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 16, | ||||
|         "pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$", | ||||
|         "description": "Document Number", | ||||
|         "validationMsg": "Document Number should not be starting with 0, / and -" | ||||
|       }, | ||||
|       "Dt": { | ||||
|         "type": "string", | ||||
|         "minLength": 10, | ||||
|         "maxLength": 10, | ||||
|         "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|         "description": "Document Date" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["Typ", "No", "Dt"] | ||||
|   }, | ||||
|   "SellerDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Gstin": { | ||||
|         "type": "string", | ||||
|         "minLength": 15, | ||||
|         "maxLength": 15, | ||||
|         "pattern": "([0-9]{2}[0-9A-Z]{13})", | ||||
|         "description": "Supplier GSTIN", | ||||
|         "validationMsg": "Company GSTIN is invalid" | ||||
|       }, | ||||
|       "LglNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Legal Name" | ||||
|       }, | ||||
|       "TrdNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Tradename" | ||||
|       }, | ||||
|       "Addr1": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 1" | ||||
|       }, | ||||
|       "Addr2": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 2" | ||||
|       }, | ||||
|       "Loc": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 50, | ||||
|         "description": "Location" | ||||
|       }, | ||||
|       "Pin": { | ||||
|         "type": "number", | ||||
|         "minimum": 100000, | ||||
|         "maximum": 999999, | ||||
|         "description": "Pincode" | ||||
|       }, | ||||
|       "Stcd": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 2, | ||||
|         "description": "Supplier State Code" | ||||
|       }, | ||||
|       "Ph": { | ||||
|         "type": "string", | ||||
|         "minLength": 6, | ||||
|         "maxLength": 12, | ||||
|         "description": "Phone" | ||||
|       }, | ||||
|       "Em": { | ||||
|         "type": "string", | ||||
|         "minLength": 6, | ||||
|         "maxLength": 100, | ||||
|         "description": "Email-Id" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["Gstin", "LglNm", "Addr1", "Loc", "Pin", "Stcd"] | ||||
|   }, | ||||
|   "BuyerDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Gstin": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 15, | ||||
|         "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$", | ||||
|         "description": "Buyer GSTIN", | ||||
|         "validationMsg": "Customer GSTIN is invalid" | ||||
|       }, | ||||
|       "LglNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Legal Name" | ||||
|       }, | ||||
|       "TrdNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Trade Name" | ||||
|       }, | ||||
|       "Pos": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 2, | ||||
|         "description": "Place of Supply State code" | ||||
|       }, | ||||
|       "Addr1": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 1" | ||||
|       }, | ||||
|       "Addr2": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 2" | ||||
|       }, | ||||
|       "Loc": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Location" | ||||
|       }, | ||||
|       "Pin": { | ||||
|         "type": "number", | ||||
|         "minimum": 100000, | ||||
|         "maximum": 999999, | ||||
|         "description": "Pincode" | ||||
|       }, | ||||
|       "Stcd": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 2, | ||||
|         "description": "Buyer State Code" | ||||
|       }, | ||||
|       "Ph": { | ||||
|         "type": "string", | ||||
|         "minLength": 6, | ||||
|         "maxLength": 12, | ||||
|         "description": "Phone" | ||||
|       }, | ||||
|       "Em": { | ||||
|         "type": "string", | ||||
|         "minLength": 6, | ||||
|         "maxLength": 100, | ||||
|         "description": "Email-Id" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["Gstin", "LglNm", "Pos", "Addr1", "Loc", "Stcd"] | ||||
|   }, | ||||
|   "DispDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Nm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Dispatch Address Name" | ||||
|       }, | ||||
|       "Addr1": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 1" | ||||
|       }, | ||||
|       "Addr2": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 2" | ||||
|       }, | ||||
|       "Loc": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Location" | ||||
|       }, | ||||
|       "Pin": { | ||||
|         "type": "number", | ||||
|         "minimum": 100000, | ||||
|         "maximum": 999999, | ||||
|         "description": "Pincode" | ||||
|       }, | ||||
|       "Stcd": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 2, | ||||
|         "description": "State Code" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["Nm", "Addr1", "Loc", "Pin", "Stcd"] | ||||
|   }, | ||||
|   "ShipDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Gstin": { | ||||
|         "type": "string", | ||||
|         "maxLength": 15, | ||||
|         "minLength": 3, | ||||
|         "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$", | ||||
|         "description": "Shipping Address GSTIN", | ||||
|         "validationMsg": "Shipping Address GSTIN is invalid" | ||||
|       }, | ||||
|       "LglNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Legal Name" | ||||
|       }, | ||||
|       "TrdNm": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Trade Name" | ||||
|       }, | ||||
|       "Addr1": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 1" | ||||
|       }, | ||||
|       "Addr2": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Address Line 2" | ||||
|       }, | ||||
|       "Loc": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Location" | ||||
|       }, | ||||
|       "Pin": { | ||||
|         "type": "number", | ||||
|         "minimum": 100000, | ||||
|         "maximum": 999999, | ||||
|         "description": "Pincode" | ||||
|       }, | ||||
|       "Stcd": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 2, | ||||
|         "description": "State Code" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["LglNm", "Addr1", "Loc", "Pin", "Stcd"] | ||||
|   }, | ||||
|   "ItemList": { | ||||
|     "type": "Array", | ||||
|     "properties": { | ||||
|       "SlNo": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 6, | ||||
|         "description": "Serial No. of Item" | ||||
|       }, | ||||
|       "PrdDesc": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 300, | ||||
|         "description": "Item Name" | ||||
|       }, | ||||
|       "IsServc": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 1, | ||||
|         "enum": ["Y", "N"], | ||||
|         "description": "Is Service Item" | ||||
|       }, | ||||
|       "HsnCd": { | ||||
|         "type": "string", | ||||
|         "minLength": 4, | ||||
|         "maxLength": 8, | ||||
|         "description": "HSN Code" | ||||
|       }, | ||||
|       "Barcde": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 30, | ||||
|         "description": "Barcode" | ||||
|       }, | ||||
|       "Qty": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 9999999999.999, | ||||
|         "description": "Quantity" | ||||
|       }, | ||||
|       "FreeQty": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 9999999999.999, | ||||
|         "description": "Free Quantity" | ||||
|       }, | ||||
|       "Unit": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 8, | ||||
|         "description": "UOM" | ||||
|       }, | ||||
|       "UnitPrice": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.999, | ||||
|         "description": "Rate" | ||||
|       }, | ||||
|       "TotAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Gross Amount" | ||||
|       }, | ||||
|       "Discount": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Discount" | ||||
|       }, | ||||
|       "PreTaxVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Pre tax value" | ||||
|       }, | ||||
|       "AssAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Taxable Value" | ||||
|       }, | ||||
|       "GstRt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999.999, | ||||
|         "description": "GST Rate" | ||||
|       }, | ||||
|       "IgstAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "IGST Amount" | ||||
|       }, | ||||
|       "CgstAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "CGST Amount" | ||||
|       }, | ||||
|       "SgstAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "SGST Amount" | ||||
|       }, | ||||
|       "CesRt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999.999, | ||||
|         "description": "Cess Rate" | ||||
|       }, | ||||
|       "CesAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Cess Amount (Advalorem)" | ||||
|       }, | ||||
|       "CesNonAdvlAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Cess Amount (Non-Advalorem)" | ||||
|       }, | ||||
|       "StateCesRt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999.999, | ||||
|         "description": "State CESS Rate" | ||||
|       }, | ||||
|       "StateCesAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "State CESS Amount" | ||||
|       }, | ||||
|       "StateCesNonAdvlAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "State CESS Amount (Non Advalorem)" | ||||
|       }, | ||||
|       "OthChrg": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Other Charges" | ||||
|       }, | ||||
|       "TotItemVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Total Item Value" | ||||
|       }, | ||||
|       "OrdLineRef": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 50, | ||||
|         "description": "Order line reference" | ||||
|       }, | ||||
|       "OrgCntry": { | ||||
|         "type": "string", | ||||
|         "minLength": 2, | ||||
|         "maxLength": 2, | ||||
|         "description": "Origin Country" | ||||
|       }, | ||||
|       "PrdSlNo": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 20, | ||||
|         "description": "Serial number" | ||||
|       }, | ||||
|       "BchDtls": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "Nm": { | ||||
|             "type": "string", | ||||
|             "minLength": 3, | ||||
|             "maxLength": 20, | ||||
|             "description": "Batch number" | ||||
|           }, | ||||
|           "ExpDt": { | ||||
|             "type": "string", | ||||
|             "maxLength": 10, | ||||
|             "minLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Batch Expiry Date" | ||||
|           }, | ||||
|           "WrDt": { | ||||
|             "type": "string", | ||||
|             "maxLength": 10, | ||||
|             "minLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Warranty Date" | ||||
|           } | ||||
|         }, | ||||
|         "required": ["Nm"] | ||||
|       }, | ||||
|       "AttribDtls": { | ||||
|         "type": "Array", | ||||
|         "Attribute": { | ||||
|           "type": "object", | ||||
|           "properties": { | ||||
|             "Nm": { | ||||
|               "type": "string", | ||||
|               "minLength": 1, | ||||
|               "maxLength": 100, | ||||
|               "description": "Attribute name of the item" | ||||
|             }, | ||||
|             "Val": { | ||||
|               "type": "string", | ||||
|               "minLength": 1, | ||||
|               "maxLength": 100, | ||||
|               "description": "Attribute value of the item" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "required": [ | ||||
|       "SlNo", | ||||
|       "IsServc", | ||||
|       "HsnCd", | ||||
|       "UnitPrice", | ||||
|       "TotAmt", | ||||
|       "AssAmt", | ||||
|       "GstRt", | ||||
|       "TotItemVal" | ||||
|     ] | ||||
|   }, | ||||
|   "ValDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "AssVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Total Assessable value of all items" | ||||
|       }, | ||||
|       "CgstVal": { | ||||
|         "type": "number", | ||||
|         "maximum": 99999999999999.99, | ||||
|         "minimum": 0, | ||||
|         "description": "Total CGST value of all items" | ||||
|       }, | ||||
|       "SgstVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Total SGST value of all items" | ||||
|       }, | ||||
|       "IgstVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Total IGST value of all items" | ||||
|       }, | ||||
|       "CesVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Total CESS value of all items" | ||||
|       }, | ||||
|       "StCesVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Total State CESS value of all items" | ||||
|       }, | ||||
|       "Discount": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Invoice Discount" | ||||
|       }, | ||||
|       "OthChrg": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Other Charges" | ||||
|       }, | ||||
|       "RndOffAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": -99.99, | ||||
|         "maximum": 99.99, | ||||
|         "description": "Rounded off Amount" | ||||
|       }, | ||||
|       "TotInvVal": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Final Invoice Value " | ||||
|       }, | ||||
|       "TotInvValFc": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Final Invoice value in Foreign Currency" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["AssVal", "TotInvVal"] | ||||
|   }, | ||||
|   "PayDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "Nm": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Payee Name" | ||||
|       }, | ||||
|       "AccDet": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 18, | ||||
|         "description": "Bank Account Number of Payee" | ||||
|       }, | ||||
|       "Mode": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 18, | ||||
|         "description": "Mode of Payment" | ||||
|       }, | ||||
|       "FinInsBr": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 11, | ||||
|         "description": "Branch or IFSC code" | ||||
|       }, | ||||
|       "PayTerm": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Terms of Payment" | ||||
|       }, | ||||
|       "PayInstr": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Payment Instruction" | ||||
|       }, | ||||
|       "CrTrn": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Credit Transfer" | ||||
|       }, | ||||
|       "DirDr": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 100, | ||||
|         "description": "Direct Debit" | ||||
|       }, | ||||
|       "CrDay": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 9999, | ||||
|         "description": "Credit Days" | ||||
|       }, | ||||
|       "PaidAmt": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Advance Amount" | ||||
|       }, | ||||
|       "PaymtDue": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 99999999999999.99, | ||||
|         "description": "Outstanding Amount" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "RefDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "InvRm": { | ||||
|         "type": "string", | ||||
|         "maxLength": 100, | ||||
|         "minLength": 3, | ||||
|         "pattern": "^[0-9A-Za-z/-]{3,100}$", | ||||
|         "description": "Remarks/Note" | ||||
|       }, | ||||
|       "DocPerdDtls": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "InvStDt": { | ||||
|             "type": "string", | ||||
|             "maxLength": 10, | ||||
|             "minLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Invoice Period Start Date" | ||||
|           }, | ||||
|           "InvEndDt": { | ||||
|             "type": "string", | ||||
|             "maxLength": 10, | ||||
|             "minLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Invoice Period End Date" | ||||
|           } | ||||
|         }, | ||||
|         "required": ["InvStDt ", "InvEndDt "] | ||||
|       }, | ||||
|       "PrecDocDtls": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "InvNo": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 16, | ||||
|             "pattern": "^[1-9A-Z]{1}[0-9A-Z/-]{1,15}$", | ||||
|             "description": "Reference of Original Invoice" | ||||
|           }, | ||||
|           "InvDt": { | ||||
|             "type": "string", | ||||
|             "maxLength": 10, | ||||
|             "minLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Date of Orginal Invoice" | ||||
|           }, | ||||
|           "OthRefNo": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "description": "Other Reference" | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "required": ["InvNo", "InvDt"], | ||||
|       "ContrDtls": { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "RecAdvRefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,20}$", | ||||
|             "description": "Receipt Advice No." | ||||
|           }, | ||||
|           "RecAdvDt": { | ||||
|             "type": "string", | ||||
|             "minLength": 10, | ||||
|             "maxLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "Date of receipt advice" | ||||
|           }, | ||||
|           "TendRefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,20}$", | ||||
|             "description": "Lot/Batch Reference No." | ||||
|           }, | ||||
|           "ContrRefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,20}$", | ||||
|             "description": "Contract Reference Number" | ||||
|           }, | ||||
|           "ExtRefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,20}$", | ||||
|             "description": "Any other reference" | ||||
|           }, | ||||
|           "ProjRefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 20, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,20}$", | ||||
|             "description": "Project Reference Number" | ||||
|           }, | ||||
|           "PORefr": { | ||||
|             "type": "string", | ||||
|             "minLength": 1, | ||||
|             "maxLength": 16, | ||||
|             "pattern": "^([0-9A-Za-z/-]){1,16}$", | ||||
|             "description": "PO Reference Number" | ||||
|           }, | ||||
|           "PORefDt": { | ||||
|             "type": "string", | ||||
|             "minLength": 10, | ||||
|             "maxLength": 10, | ||||
|             "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|             "description": "PO Reference date" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "AddlDocDtls": { | ||||
|     "type": "Array", | ||||
|     "properties": { | ||||
|       "Url": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Supporting document URL" | ||||
|       }, | ||||
|       "Docs": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 1000, | ||||
|         "description": "Supporting document in Base64 Format" | ||||
|       }, | ||||
|       "Info": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 1000, | ||||
|         "description": "Any additional information" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   "ExpDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "ShipBNo": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 20, | ||||
|         "description": "Shipping Bill No." | ||||
|       }, | ||||
|       "ShipBDt": { | ||||
|         "type": "string", | ||||
|         "minLength": 10, | ||||
|         "maxLength": 10, | ||||
|         "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|         "description": "Shipping Bill Date" | ||||
|       }, | ||||
|       "Port": { | ||||
|         "type": "string", | ||||
|         "minLength": 2, | ||||
|         "maxLength": 10, | ||||
|         "pattern": "^[0-9A-Za-z]{2,10}$", | ||||
|         "description": "Port Code. Refer the master" | ||||
|       }, | ||||
|       "RefClm": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 1, | ||||
|         "description": "Claiming Refund. Y/N" | ||||
|       }, | ||||
|       "ForCur": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 16, | ||||
|         "description": "Additional Currency Code. Refer the master" | ||||
|       }, | ||||
|       "CntCode": { | ||||
|         "type": "string", | ||||
|         "minLength": 2, | ||||
|         "maxLength": 2, | ||||
|         "description": "Country Code. Refer the master" | ||||
|       }, | ||||
|       "ExpDuty": { | ||||
|         "type": "number", | ||||
|         "minimum": 0, | ||||
|         "maximum": 999999999999.99, | ||||
|         "description": "Export Duty" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "EwbDtls": { | ||||
|     "type": "object", | ||||
|     "properties": { | ||||
|       "TransId": { | ||||
|         "type": "string", | ||||
|         "minLength": 15, | ||||
|         "maxLength": 15, | ||||
|         "description": "Transporter GSTIN" | ||||
|       }, | ||||
|       "TransName": { | ||||
|         "type": "string", | ||||
|         "minLength": 3, | ||||
|         "maxLength": 100, | ||||
|         "description": "Transporter Name" | ||||
|       }, | ||||
|       "TransMode": { | ||||
|         "type": "string", | ||||
|         "maxLength": 1, | ||||
|         "minLength": 1, | ||||
|         "enum": ["1", "2", "3", "4"], | ||||
|         "description": "Mode of Transport" | ||||
|       }, | ||||
|       "Distance": { | ||||
|         "type": "number", | ||||
|         "minimum": 1, | ||||
|         "maximum": 9999, | ||||
|         "description": "Distance" | ||||
|       }, | ||||
|       "TransDocNo": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 15, | ||||
|         "pattern": "^([0-9A-Z/-]){1,15}$", | ||||
|         "description": "Tranport Document Number", | ||||
|         "validationMsg": "Transport Receipt No is invalid" | ||||
|       }, | ||||
|       "TransDocDt": { | ||||
|         "type": "string", | ||||
|         "minLength": 10, | ||||
|         "maxLength": 10, | ||||
|         "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", | ||||
|         "description": "Transport Document Date" | ||||
|       }, | ||||
|       "VehNo": { | ||||
|         "type": "string", | ||||
|         "minLength": 4, | ||||
|         "maxLength": 20, | ||||
|         "description": "Vehicle Number" | ||||
|       }, | ||||
|       "VehType": { | ||||
|         "type": "string", | ||||
|         "minLength": 1, | ||||
|         "maxLength": 1, | ||||
|         "enum": ["O", "R"], | ||||
|         "description": "Vehicle Type" | ||||
|       } | ||||
|     }, | ||||
|     "required": ["Distance"] | ||||
|   }, | ||||
|   "required": [ | ||||
|     "Version", | ||||
|     "TranDtls", | ||||
|     "DocDtls", | ||||
|     "SellerDtls", | ||||
|     "BuyerDtls", | ||||
|     "ItemList", | ||||
|     "ValDtls" | ||||
|   ] | ||||
| } | ||||
| @ -1,292 +0,0 @@ | ||||
| erpnext.setup_einvoice_actions = (doctype) => { | ||||
| 	frappe.ui.form.on(doctype, { | ||||
| 		async refresh(frm) { | ||||
| 			if (frm.doc.docstatus == 2) return; | ||||
| 
 | ||||
| 			const res = await frappe.call({ | ||||
| 				method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility', | ||||
| 				args: { doc: frm.doc } | ||||
| 			}); | ||||
| 			const invoice_eligible = res.message; | ||||
| 
 | ||||
| 			if (!invoice_eligible) return; | ||||
| 
 | ||||
| 			const { doctype, 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 (!irn && !__unsaved) { | ||||
| 				const action = () => { | ||||
| 					if (frm.doc.__unsaved) { | ||||
| 						frappe.throw(__('Please save the document to generate IRN.')); | ||||
| 					} | ||||
| 					frappe.call({ | ||||
| 						method: 'erpnext.regional.india.e_invoice.utils.get_einvoice', | ||||
| 						args: { doctype, docname: name }, | ||||
| 						freeze: true, | ||||
| 						callback: (res) => { | ||||
| 							const einvoice = res.message; | ||||
| 							show_einvoice_preview(frm, einvoice); | ||||
| 						} | ||||
| 					}); | ||||
| 				}; | ||||
| 
 | ||||
| 				add_custom_button(__("Generate IRN"), action); | ||||
| 			} | ||||
| 
 | ||||
| 			if (irn && !irn_cancelled && !ewaybill) { | ||||
| 				const fields = [ | ||||
| 					{ | ||||
| 						"label": "Reason", | ||||
| 						"fieldname": "reason", | ||||
| 						"fieldtype": "Select", | ||||
| 						"reqd": 1, | ||||
| 						"default": "1-Duplicate", | ||||
| 						"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] | ||||
| 					}, | ||||
| 					{ | ||||
| 						"label": "Remark", | ||||
| 						"fieldname": "remark", | ||||
| 						"fieldtype": "Data", | ||||
| 						"reqd": 1 | ||||
| 					} | ||||
| 				]; | ||||
| 				const action = () => { | ||||
| 					const d = new frappe.ui.Dialog({ | ||||
| 						title: __("Cancel IRN"), | ||||
| 						fields: fields, | ||||
| 						primary_action: function() { | ||||
| 							const data = d.get_values(); | ||||
| 							frappe.call({ | ||||
| 								method: 'erpnext.regional.india.e_invoice.utils.cancel_irn', | ||||
| 								args: { | ||||
| 									doctype, | ||||
| 									docname: name, | ||||
| 									irn: irn, | ||||
| 									reason: data.reason.split('-')[0], | ||||
| 									remark: data.remark | ||||
| 								}, | ||||
| 								freeze: true, | ||||
| 								callback: () => frm.reload_doc() || d.hide(), | ||||
| 								error: () => d.hide() | ||||
| 							}); | ||||
| 						}, | ||||
| 						primary_action_label: __('Submit') | ||||
| 					}); | ||||
| 					d.show(); | ||||
| 				}; | ||||
| 				add_custom_button(__("Cancel IRN"), action); | ||||
| 			} | ||||
| 
 | ||||
| 			if (irn && !irn_cancelled && !ewaybill) { | ||||
| 				const action = () => { | ||||
| 					const d = new frappe.ui.Dialog({ | ||||
| 						title: __('Generate E-Way Bill'), | ||||
| 						size: "large", | ||||
| 						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: { | ||||
| 									doctype, | ||||
| 									docname: name, | ||||
| 									irn, | ||||
| 									...data | ||||
| 								}, | ||||
| 								freeze: true, | ||||
| 								callback: () => frm.reload_doc() || d.hide(), | ||||
| 								error: () => d.hide() | ||||
| 							}); | ||||
| 						}, | ||||
| 						primary_action_label: __('Submit') | ||||
| 					}); | ||||
| 					d.show(); | ||||
| 				}; | ||||
| 
 | ||||
| 				add_custom_button(__("Generate E-Way Bill"), action); | ||||
| 			} | ||||
| 
 | ||||
| 			if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) { | ||||
| 				const action = () => { | ||||
| 					let message = __('Cancellation of e-way bill is currently not supported.') + ' '; | ||||
| 					message += '<br><br>'; | ||||
| 					message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.'); | ||||
| 
 | ||||
| 					const dialog = frappe.msgprint({ | ||||
| 						title: __('Update E-Way Bill Cancelled Status?'), | ||||
| 						message: message, | ||||
| 						indicator: 'orange', | ||||
| 						primary_action: { | ||||
| 							action: function() { | ||||
| 								frappe.call({ | ||||
| 									method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill', | ||||
| 									args: { doctype, docname: name }, | ||||
| 									freeze: true, | ||||
| 									callback: () => frm.reload_doc() || dialog.hide() | ||||
| 								}); | ||||
| 							} | ||||
| 						}, | ||||
| 						primary_action_label: __('Yes') | ||||
| 					}); | ||||
| 				}; | ||||
| 				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', | ||||
| 			'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 request_irn_generation = (frm) => { | ||||
| 	frappe.call({ | ||||
| 		method: 'erpnext.regional.india.e_invoice.utils.generate_irn', | ||||
| 		args: { doctype: frm.doc.doctype, docname: frm.doc.name }, | ||||
| 		freeze: true, | ||||
| 		callback: () => frm.reload_doc() | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| const get_preview_dialog = (frm, action) => { | ||||
| 	const dialog = new frappe.ui.Dialog({ | ||||
| 		title: __("Preview"), | ||||
| 		size: "large", | ||||
| 		fields: [ | ||||
| 			{ | ||||
| 				"label": "Preview", | ||||
| 				"fieldname": "preview_html", | ||||
| 				"fieldtype": "HTML" | ||||
| 			} | ||||
| 		], | ||||
| 		primary_action: () => action(frm) || dialog.hide(), | ||||
| 		primary_action_label: __('Generate IRN') | ||||
| 	}); | ||||
| 	return dialog; | ||||
| }; | ||||
| 
 | ||||
| const show_einvoice_preview = (frm, einvoice) => { | ||||
| 	const preview_dialog = get_preview_dialog(frm, request_irn_generation); | ||||
| 
 | ||||
| 	// initialize e-invoice fields
 | ||||
| 	einvoice["Irn"] = einvoice["AckNo"] = ''; einvoice["AckDt"] = frappe.datetime.nowdate(); | ||||
| 	frm.doc.signed_einvoice = JSON.stringify(einvoice); | ||||
| 
 | ||||
| 	// initialize preview wrapper
 | ||||
| 	const $preview_wrapper = preview_dialog.get_field("preview_html").$wrapper; | ||||
| 	$preview_wrapper.html( | ||||
| 		`<div>
 | ||||
| 			<div class="print-preview"> | ||||
| 				<div class="print-format"></div> | ||||
| 			</div> | ||||
| 			<div class="page-break-message text-muted text-center text-medium margin-top"></div> | ||||
| 		</div>` | ||||
| 	); | ||||
| 
 | ||||
| 	frappe.call({ | ||||
| 		method: "frappe.www.printview.get_html_and_style", | ||||
| 		args: { | ||||
| 			doc: frm.doc, | ||||
| 			print_format: "GST E-Invoice", | ||||
| 			no_letterhead: 1 | ||||
| 		}, | ||||
| 		callback: function (r) { | ||||
| 			if (!r.exc) { | ||||
| 				$preview_wrapper.find(".print-format").html(r.message.html); | ||||
| 				const style = ` | ||||
| 					.print-format { box-shadow: 0px 0px 5px rgba(0,0,0,0.2); padding: 0.30in; min-height: 80vh; } | ||||
| 					.print-preview { min-height: 0px; } | ||||
| 					.modal-dialog { width: 720px; }`;
 | ||||
| 
 | ||||
| 				frappe.dom.set_style(style, "custom-print-style"); | ||||
| 				preview_dialog.show(); | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
| }; | ||||
| @ -61,7 +61,7 @@ def create_hsn_codes(data, code_field): | ||||
| 
 | ||||
| def add_custom_roles_for_reports(): | ||||
| 	for report_name in ('GST Sales Register', 'GST Purchase Register', | ||||
| 		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill', 'E-Invoice Summary'): | ||||
| 		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill'): | ||||
| 
 | ||||
| 		if not frappe.db.get_value('Custom Role', dict(report=report_name)): | ||||
| 			frappe.get_doc(dict( | ||||
| @ -100,7 +100,7 @@ def add_custom_roles_for_reports(): | ||||
| 			)).insert() | ||||
| 
 | ||||
| def add_permissions(): | ||||
| 	for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate', 'E Invoice Settings'): | ||||
| 	for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): | ||||
| 		add_permission(doctype, 'All', 0) | ||||
| 		for role in ('Accounts Manager', 'Accounts User', 'System Manager'): | ||||
| 			add_permission(doctype, role, 0) | ||||
| @ -116,11 +116,9 @@ def add_permissions(): | ||||
| def add_print_formats(): | ||||
| 	frappe.reload_doc("regional", "print_format", "gst_tax_invoice") | ||||
| 	frappe.reload_doc("accounts", "print_format", "gst_pos_invoice") | ||||
| 	frappe.reload_doc("accounts", "print_format", "GST E-Invoice") | ||||
| 
 | ||||
| 	frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0) | ||||
| 	frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0) | ||||
| 	frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0) | ||||
| 
 | ||||
| def make_property_setters(patch=False): | ||||
| 	# GST rules do not allow for an invoice no. bigger than 16 characters | ||||
| @ -445,53 +443,13 @@ def make_custom_fields(update=True): | ||||
| 			'fieldname': 'ewaybill', | ||||
| 			'label': 'E-Way Bill No.', | ||||
| 			'fieldtype': 'Data', | ||||
| 			'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)', | ||||
| 			'depends_on': 'eval:(doc.docstatus === 1)', | ||||
| 			'allow_on_submit': 1, | ||||
| 			'insert_after': 'tax_id', | ||||
| 			'translatable': 0 | ||||
| 		} | ||||
| 	] | ||||
| 
 | ||||
| 	si_einvoice_fields = [ | ||||
| 		dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1, | ||||
| 			depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'), | ||||
| 
 | ||||
| 		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, | ||||
| 			depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'), | ||||
| 
 | ||||
| 		dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1, | ||||
| 			depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'), | ||||
| 
 | ||||
| 		dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1, | ||||
| 			depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), | ||||
| 
 | ||||
| 		dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type', | ||||
| 			print_hide=1, hidden=1), | ||||
| 
 | ||||
| 		dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section', | ||||
| 			no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 		dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 		dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', | ||||
| 			no_copy=1, print_hide=1), | ||||
| 
 | ||||
| 		dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date', | ||||
| 			no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 		dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice', | ||||
| 			no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 		dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code', | ||||
| 			no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 		dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image', | ||||
| 			options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1), | ||||
| 
 | ||||
| 		dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON', | ||||
| 			hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1) | ||||
| 	] | ||||
| 
 | ||||
| 	custom_fields = { | ||||
| 		'Address': [ | ||||
| 			dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data', | ||||
| @ -504,7 +462,7 @@ def make_custom_fields(update=True): | ||||
| 		'Purchase Invoice': purchase_invoice_gst_category + invoice_gst_fields + purchase_invoice_itc_fields + purchase_invoice_gst_fields, | ||||
| 		'Purchase Order': purchase_invoice_gst_fields, | ||||
| 		'Purchase Receipt': purchase_invoice_gst_fields, | ||||
| 		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields, | ||||
| 		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields, | ||||
| 		'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category, | ||||
| 		'Journal Entry': journal_entry_fields, | ||||
| 		'Sales Order': sales_invoice_gst_fields, | ||||
|  | ||||
| @ -475,7 +475,7 @@ def get_ewb_data(dt, dn): | ||||
| 		ewaybills.append(data) | ||||
| 
 | ||||
| 	data = { | ||||
| 		'version': '1.0.1118', | ||||
| 		'version': '1.0.0421', | ||||
| 		'billLists': ewaybills | ||||
| 	} | ||||
| 
 | ||||
| @ -871,3 +871,20 @@ def set_item_tax_from_hsn_code(item): | ||||
| 				'tax_category': tax.tax_category, | ||||
| 				'valid_from': tax.valid_from | ||||
| 			}) | ||||
| 
 | ||||
| def delete_gst_settings_for_company(doc, method): | ||||
| 	if doc.country != 'India': | ||||
| 		return | ||||
| 
 | ||||
| 	gst_settings = frappe.get_doc("GST Settings") | ||||
| 	records_to_delete = [] | ||||
| 
 | ||||
| 	for d in reversed(gst_settings.get('gst_accounts')): | ||||
| 		if d.company == doc.name: | ||||
| 			records_to_delete.append(d) | ||||
| 
 | ||||
| 	for d in records_to_delete: | ||||
| 		gst_settings.remove(d) | ||||
| 
 | ||||
| 	gst_settings.save() | ||||
| 
 | ||||
|  | ||||
| @ -1,55 +0,0 @@ | ||||
| // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 | ||||
| // For license information, please see license.txt
 | ||||
| /* eslint-disable */ | ||||
| 
 | ||||
| frappe.query_reports["E-Invoice Summary"] = { | ||||
| 	"filters": [ | ||||
| 		{ | ||||
| 			"fieldtype": "Link", | ||||
| 			"options": "Company", | ||||
| 			"reqd": 1, | ||||
| 			"fieldname": "company", | ||||
| 			"label": __("Company"), | ||||
| 			"default": frappe.defaults.get_user_default("Company"), | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Link", | ||||
| 			"options": "Customer", | ||||
| 			"fieldname": "customer", | ||||
| 			"label": __("Customer") | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Date", | ||||
| 			"reqd": 1, | ||||
| 			"fieldname": "from_date", | ||||
| 			"label": __("From Date"), | ||||
| 			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Date", | ||||
| 			"reqd": 1, | ||||
| 			"fieldname": "to_date", | ||||
| 			"label": __("To Date"), | ||||
| 			"default": frappe.datetime.get_today(), | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Select", | ||||
| 			"fieldname": "status", | ||||
| 			"label": __("Status"), | ||||
| 			"options": "\nPending\nGenerated\nCancelled\nFailed" | ||||
| 		} | ||||
| 	], | ||||
| 
 | ||||
| 	"formatter": function (value, row, column, data, default_formatter) { | ||||
| 		value = default_formatter(value, row, column, data); | ||||
| 
 | ||||
| 		if (column.fieldname == "einvoice_status" && value) { | ||||
| 			if (value == 'Pending') value = `<span class="bold" style="color: var(--text-on-orange)">${value}</span>`; | ||||
| 			else if (value == 'Generated') value = `<span class="bold" style="color: var(--text-on-green)">${value}</span>`; | ||||
| 			else if (value == 'Cancelled') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`; | ||||
| 			else if (value == 'Failed') value = `<span class="bold"  style="color: var(--text-on-red)">${value}</span>`; | ||||
| 		} | ||||
| 
 | ||||
| 		return value; | ||||
| 	} | ||||
| }; | ||||
| @ -1,28 +0,0 @@ | ||||
| { | ||||
|  "add_total_row": 0, | ||||
|  "columns": [], | ||||
|  "creation": "2021-03-12 11:23:37.312294", | ||||
|  "disable_prepared_report": 0, | ||||
|  "disabled": 0, | ||||
|  "docstatus": 0, | ||||
|  "doctype": "Report", | ||||
|  "filters": [], | ||||
|  "idx": 0, | ||||
|  "is_standard": "Yes", | ||||
|  "json": "{}", | ||||
|  "letter_head": "Logo", | ||||
|  "modified": "2021-03-13 12:36:48.689413", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Regional", | ||||
|  "name": "E-Invoice Summary", | ||||
|  "owner": "Administrator", | ||||
|  "prepared_report": 0, | ||||
|  "ref_doctype": "Sales Invoice", | ||||
|  "report_name": "E-Invoice Summary", | ||||
|  "report_type": "Script Report", | ||||
|  "roles": [ | ||||
|   { | ||||
|    "role": "Administrator" | ||||
|   } | ||||
|  ] | ||||
| } | ||||
| @ -1,106 +0,0 @@ | ||||
| # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors | ||||
| # For license information, please see license.txt | ||||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe import _ | ||||
| 
 | ||||
| def execute(filters=None): | ||||
| 	validate_filters(filters) | ||||
| 
 | ||||
| 	columns = get_columns() | ||||
| 	data = get_data(filters) | ||||
| 
 | ||||
| 	return columns, data | ||||
| 
 | ||||
| def validate_filters(filters={}): | ||||
| 	filters = frappe._dict(filters) | ||||
| 
 | ||||
| 	if not filters.company: | ||||
| 		frappe.throw(_('{} is mandatory for generating E-Invoice Summary Report').format(_('Company')), title=_('Invalid Filter')) | ||||
| 	if filters.company: | ||||
| 		# validate if company has e-invoicing enabled | ||||
| 		pass | ||||
| 	if not filters.from_date or not filters.to_date: | ||||
| 		frappe.throw(_('From Date & To Date is mandatory for generating E-Invoice Summary Report'), title=_('Invalid Filter')) | ||||
| 	if filters.from_date > filters.to_date: | ||||
| 		frappe.throw(_('From Date must be before To Date'), title=_('Invalid Filter')) | ||||
| 
 | ||||
| def get_data(filters={}): | ||||
| 	query_filters = { | ||||
| 		'posting_date': ['between', [filters.from_date, filters.to_date]], | ||||
| 		'einvoice_status': ['is', 'set'], | ||||
| 		'company': filters.company | ||||
| 	} | ||||
| 	if filters.customer: | ||||
| 		query_filters['customer'] = filters.customer | ||||
| 	if filters.status: | ||||
| 		query_filters['einvoice_status'] = filters.status | ||||
| 
 | ||||
| 	data = frappe.get_all( | ||||
| 		'Sales Invoice', | ||||
| 		filters=query_filters, | ||||
| 		fields=[d.get('fieldname') for d in get_columns()] | ||||
| 	) | ||||
| 
 | ||||
| 	return data | ||||
| 
 | ||||
| def get_columns(): | ||||
| 	return [ | ||||
| 		{ | ||||
| 			"fieldtype": "Date", | ||||
| 			"fieldname": "posting_date", | ||||
| 			"label": _("Posting Date"), | ||||
| 			"width": 0 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Link", | ||||
| 			"fieldname": "name", | ||||
| 			"label": _("Sales Invoice"), | ||||
| 			"options": "Sales Invoice", | ||||
| 			"width": 140 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Data", | ||||
| 			"fieldname": "einvoice_status", | ||||
| 			"label": _("Status"), | ||||
| 			"width": 100 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Link", | ||||
| 			"fieldname": "customer", | ||||
| 			"options": "Customer", | ||||
| 			"label": _("Customer") | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Check", | ||||
| 			"fieldname": "is_return", | ||||
| 			"label": _("Is Return"), | ||||
| 			"width": 85 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Data", | ||||
| 			"fieldname": "ack_no", | ||||
| 			"label": "Ack. No.", | ||||
| 			"width": 145 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Data", | ||||
| 			"fieldname": "ack_date", | ||||
| 			"label": "Ack. Date", | ||||
| 			"width": 165 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Data", | ||||
| 			"fieldname": "irn", | ||||
| 			"label": _("IRN No."), | ||||
| 			"width": 250 | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fieldtype": "Currency", | ||||
| 			"options": "Company:company:default_currency", | ||||
| 			"fieldname": "base_grand_total", | ||||
| 			"label": _("Grand Total"), | ||||
| 			"width": 120 | ||||
| 		} | ||||
| 	] | ||||
| @ -571,7 +571,8 @@ | ||||
|    "fieldtype": "Data", | ||||
|    "hide_days": 1, | ||||
|    "hide_seconds": 1, | ||||
|    "label": "Scan Barcode" | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 1, | ||||
| @ -1510,7 +1511,7 @@ | ||||
|  "idx": 105, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-07-08 21:37:44.177493", | ||||
|  "modified": "2021-08-17 20:15:26.531553", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Selling", | ||||
|  "name": "Sales Order", | ||||
|  | ||||
| @ -44,6 +44,18 @@ def get_data(filters, period_list, partner_doctype): | ||||
| 		if d.item_group not in item_groups: | ||||
| 			item_groups.append(d.item_group) | ||||
| 
 | ||||
| 	if item_groups: | ||||
| 		child_items = [] | ||||
| 		for item_group in item_groups: | ||||
| 			if frappe.db.get_value("Item Group", {"name":item_group}, "is_group"): | ||||
| 				for child_item_group in frappe.get_all("Item Group", {"parent_item_group":item_group}): | ||||
| 					if child_item_group['name'] not in child_items: | ||||
| 						child_items.append(child_item_group['name']) | ||||
| 
 | ||||
| 		for item in child_items: | ||||
| 			if item not in item_groups: | ||||
| 				item_groups.append(item) | ||||
| 
 | ||||
| 	date_field = ("transaction_date" | ||||
| 		if filters.get('doctype') == "Sales Order" else "posting_date") | ||||
| 
 | ||||
|  | ||||
| @ -394,6 +394,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran | ||||
| 	} | ||||
| 
 | ||||
| 	_set_batch_number(doc) { | ||||
| 		if (doc.batch_no) { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}; | ||||
| 		if (doc.has_serial_no && doc.serial_no) { | ||||
| 			args['serial_no'] = doc.serial_no | ||||
|  | ||||
| @ -393,6 +393,10 @@ class Company(NestedSet): | ||||
| 		frappe.db.sql("delete from `tabPurchase Taxes and Charges Template` where company=%s", self.name) | ||||
| 		frappe.db.sql("delete from `tabItem Tax Template` where company=%s", self.name) | ||||
| 
 | ||||
| 		# delete Process Deferred Accounts if no GL Entry found | ||||
| 		if not frappe.db.get_value('GL Entry', {'company': self.name}): | ||||
| 			frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name) | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def enqueue_replace_abbr(company, old, new): | ||||
| 	kwargs = dict(queue="long", company=company, old=old, new=new) | ||||
|  | ||||
| @ -308,7 +308,7 @@ def update_party(fullname, company_name=None, mobile_no=None, phone=None): | ||||
| 	party = get_party() | ||||
| 
 | ||||
| 	party.customer_name = company_name or fullname | ||||
| 	party.customer_type == "Company" if company_name else "Individual" | ||||
| 	party.customer_type = "Company" if company_name else "Individual" | ||||
| 
 | ||||
| 	contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) | ||||
| 	contact = frappe.get_doc("Contact", contact_name) | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe import _, msgprint | ||||
| from frappe.utils import comma_and | ||||
| from frappe.utils import flt | ||||
| from frappe.model.document import Document | ||||
| from frappe.utils import get_datetime, get_datetime_str, now_datetime | ||||
| 
 | ||||
| @ -18,46 +18,35 @@ class ShoppingCartSettings(Document): | ||||
| 
 | ||||
| 	def validate(self): | ||||
| 		if self.enabled: | ||||
| 			self.validate_exchange_rates_exist() | ||||
| 			self.validate_price_list_exchange_rate() | ||||
| 
 | ||||
| 	def validate_price_list_exchange_rate(self): | ||||
| 		"Check if exchange rate exists for Price List currency (to Company's currency)." | ||||
| 		from erpnext.setup.utils import get_exchange_rate | ||||
| 
 | ||||
| 		if not self.enabled or not self.company or not self.price_list: | ||||
| 			return # this function is also called from hooks, check values again | ||||
| 
 | ||||
| 		company_currency = frappe.get_cached_value("Company", self.company, "default_currency") | ||||
| 		price_list_currency = frappe.db.get_value("Price List", self.price_list, "currency") | ||||
| 
 | ||||
| 	def validate_exchange_rates_exist(self): | ||||
| 		"""check if exchange rates exist for all Price List currencies (to company's currency)""" | ||||
| 		company_currency = frappe.get_cached_value('Company',  self.company,  "default_currency") | ||||
| 		if not company_currency: | ||||
| 			msgprint(_("Please specify currency in Company") + ": " + self.company, | ||||
| 				raise_exception=ShoppingCartSetupError) | ||||
| 			msg = f"Please specify currency in Company {self.company}" | ||||
| 			frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError) | ||||
| 
 | ||||
| 		price_list_currency_map = frappe.db.get_values("Price List", | ||||
| 			[self.price_list], "currency") | ||||
| 		if not price_list_currency: | ||||
| 			msg = f"Please specify currency in Price List {frappe.bold(self.price_list)}" | ||||
| 			frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError) | ||||
| 
 | ||||
| 		price_list_currency_map = dict(price_list_currency_map) | ||||
| 		if price_list_currency != company_currency: | ||||
| 			from_currency, to_currency = price_list_currency, company_currency | ||||
| 
 | ||||
| 		# check if all price lists have a currency | ||||
| 		for price_list, currency in price_list_currency_map.items(): | ||||
| 			if not currency: | ||||
| 				frappe.throw(_("Currency is required for Price List {0}").format(price_list)) | ||||
| 			# Get exchange rate checks Currency Exchange Records too | ||||
| 			exchange_rate = get_exchange_rate(from_currency, to_currency, args="for_selling") | ||||
| 
 | ||||
| 		expected_to_exist = [currency + "-" + company_currency | ||||
| 			for currency in price_list_currency_map.values() | ||||
| 			if currency != company_currency] | ||||
| 
 | ||||
| 		# manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange | ||||
| 		from_currency = [currency for currency in price_list_currency_map.values() if currency != company_currency] | ||||
| 		to_currency = company_currency | ||||
| 		# manqala end | ||||
| 
 | ||||
| 		if expected_to_exist: | ||||
| 			# manqala 20/09/2016: modify query so that it uses date in the selection from Currency Exchange. | ||||
| 			# exchange rates defined with date less than the date on which this document is being saved will be selected | ||||
| 			exists = frappe.db.sql_list("""select CONCAT(from_currency,'-',to_currency) from `tabCurrency Exchange` | ||||
| 				where from_currency in (%s) and to_currency = "%s" and date <= curdate()""" % (", ".join(["%s"]*len(from_currency)), to_currency), tuple(from_currency)) | ||||
| 			# manqala end | ||||
| 
 | ||||
| 			missing = list(set(expected_to_exist).difference(exists)) | ||||
| 
 | ||||
| 			if missing: | ||||
| 				msgprint(_("Missing Currency Exchange Rates for {0}").format(comma_and(missing)), | ||||
| 					raise_exception=ShoppingCartSetupError) | ||||
| 			if not flt(exchange_rate): | ||||
| 				msg = f"Missing Currency Exchange Rates for {from_currency}-{to_currency}" | ||||
| 				frappe.throw(_(msg), title=_("Missing"), exc=ShoppingCartSetupError) | ||||
| 
 | ||||
| 	def validate_tax_rule(self): | ||||
| 		if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart" : 1}, "name"): | ||||
| @ -71,7 +60,7 @@ class ShoppingCartSettings(Document): | ||||
| 	def get_shipping_rules(self, shipping_territory): | ||||
| 		return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule") | ||||
| 
 | ||||
| def validate_cart_settings(doc, method): | ||||
| def validate_cart_settings(doc=None, method=None): | ||||
| 	frappe.get_doc("Shopping Cart Settings", "Shopping Cart Settings").run_method("validate") | ||||
| 
 | ||||
| def get_shopping_cart_settings(): | ||||
|  | ||||
| @ -16,17 +16,25 @@ class TestShoppingCartSettings(unittest.TestCase): | ||||
| 		return frappe.get_doc({"doctype": "Shopping Cart Settings", | ||||
| 			"company": "_Test Company"}) | ||||
| 
 | ||||
| 	def test_exchange_rate_exists(self): | ||||
| 		frappe.db.sql("""delete from `tabCurrency Exchange`""") | ||||
| 	# NOTE: Exchangrate API has all enabled currencies that ERPNext supports. | ||||
| 	# We aren't checking just currency exchange record anymore | ||||
| 	# while validating price list currency exchange rate to that of company. | ||||
| 	# The API is being used to fetch the rate which again almost always | ||||
| 	# gives back a valid value (for valid currencies). | ||||
| 	# This makes the test obsolete. | ||||
| 	# Commenting because im not sure if there's a better test we can write | ||||
| 
 | ||||
| 		cart_settings = self.get_cart_settings() | ||||
| 		cart_settings.price_list = "_Test Price List Rest of the World" | ||||
| 		self.assertRaises(ShoppingCartSetupError, cart_settings.validate_exchange_rates_exist) | ||||
| 	# def test_exchange_rate_exists(self): | ||||
| 	# 	frappe.db.sql("""delete from `tabCurrency Exchange`""") | ||||
| 
 | ||||
| 		from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ | ||||
| 			currency_exchange_records | ||||
| 		frappe.get_doc(currency_exchange_records[0]).insert() | ||||
| 		cart_settings.validate_exchange_rates_exist() | ||||
| 	# 	cart_settings = self.get_cart_settings() | ||||
| 	# 	cart_settings.price_list = "_Test Price List Rest of the World" | ||||
| 	# 	self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate) | ||||
| 
 | ||||
| 	# 	from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ | ||||
| 	# 		currency_exchange_records | ||||
| 	# 	frappe.get_doc(currency_exchange_records[0]).insert() | ||||
| 	# 	cart_settings.validate_price_list_exchange_rate() | ||||
| 
 | ||||
| 	def test_tax_rule_validation(self): | ||||
| 		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0") | ||||
|  | ||||
| @ -515,7 +515,8 @@ | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode" | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 1, | ||||
| @ -1305,7 +1306,7 @@ | ||||
|  "idx": 146, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-07-08 21:37:20.802652", | ||||
|  "modified": "2021-08-17 20:15:50.574966", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Stock", | ||||
|  "name": "Delivery Note", | ||||
|  | ||||
| @ -133,7 +133,8 @@ | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode" | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "allow_bulk_edit": 1, | ||||
| @ -181,7 +182,7 @@ | ||||
|    "no_copy": 1, | ||||
|    "oldfieldname": "status", | ||||
|    "oldfieldtype": "Select", | ||||
|    "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nPartially Received\nOrdered\nIssued\nTransferred\nReceived", | ||||
|    "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred\nReceived", | ||||
|    "print_hide": 1, | ||||
|    "print_width": "100px", | ||||
|    "read_only": 1, | ||||
| @ -314,7 +315,7 @@ | ||||
|  "idx": 70, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-03-31 23:52:55.392512", | ||||
|  "modified": "2021-08-17 20:16:12.737743", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Stock", | ||||
|  "name": "Material Request", | ||||
|  | ||||
| @ -13,6 +13,9 @@ class PriceList(Document): | ||||
| 		if not cint(self.buying) and not cint(self.selling): | ||||
| 			throw(_("Price List must be applicable for Buying or Selling")) | ||||
| 
 | ||||
| 		if not self.is_new(): | ||||
| 			self.check_impact_on_shopping_cart() | ||||
| 
 | ||||
| 	def on_update(self): | ||||
| 		self.set_default_if_missing() | ||||
| 		self.update_item_price() | ||||
| @ -32,6 +35,17 @@ class PriceList(Document): | ||||
| 			buying=%s, selling=%s, modified=NOW() where price_list=%s""", | ||||
| 			(self.currency, cint(self.buying), cint(self.selling), self.name)) | ||||
| 
 | ||||
| 	def check_impact_on_shopping_cart(self): | ||||
| 		"Check if Price List currency change impacts Shopping Cart." | ||||
| 		from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import validate_cart_settings | ||||
| 
 | ||||
| 		doc_before_save = self.get_doc_before_save() | ||||
| 		currency_changed = self.currency != doc_before_save.currency | ||||
| 		affects_cart = self.name == frappe.get_cached_value("Shopping Cart Settings", None, "price_list") | ||||
| 
 | ||||
| 		if currency_changed and affects_cart: | ||||
| 			validate_cart_settings() | ||||
| 
 | ||||
| 	def on_trash(self): | ||||
| 		self.delete_price_list_details_key() | ||||
| 
 | ||||
|  | ||||
| @ -1098,7 +1098,8 @@ | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode" | ||||
|    "label": "Scan Barcode", | ||||
|    "options": "Barcode" | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "billing_address", | ||||
| @ -1148,7 +1149,7 @@ | ||||
|  "idx": 261, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-05-25 00:15:12.239017", | ||||
|  "modified": "2021-08-17 20:16:40.849885", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Stock", | ||||
|  "name": "Purchase Receipt", | ||||
|  | ||||
| @ -355,6 +355,7 @@ | ||||
|   }, | ||||
|   { | ||||
|    "fieldname": "scan_barcode", | ||||
|    "options": "Barcode", | ||||
|    "fieldtype": "Data", | ||||
|    "label": "Scan Barcode" | ||||
|   }, | ||||
| @ -629,7 +630,7 @@ | ||||
|  "index_web_pages_for_search": 1, | ||||
|  "is_submittable": 1, | ||||
|  "links": [], | ||||
|  "modified": "2021-05-26 17:07:58.015737", | ||||
|  "modified": "2021-08-17 20:16:12.737743", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Stock", | ||||
|  "name": "Stock Entry", | ||||
|  | ||||
| @ -317,9 +317,6 @@ class StockEntry(StockController): | ||||
| 				d.s_warehouse = self.from_warehouse | ||||
| 				d.t_warehouse = self.to_warehouse | ||||
| 
 | ||||
| 			if not (d.s_warehouse or d.t_warehouse): | ||||
| 				frappe.throw(_("Atleast one warehouse is mandatory")) | ||||
| 
 | ||||
| 			if self.purpose in source_mandatory and not d.s_warehouse: | ||||
| 				if self.from_warehouse: | ||||
| 					d.s_warehouse = self.from_warehouse | ||||
| @ -332,6 +329,7 @@ class StockEntry(StockController): | ||||
| 				else: | ||||
| 					frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) | ||||
| 
 | ||||
| 
 | ||||
| 			if self.purpose == "Manufacture": | ||||
| 				if validate_for_manufacture: | ||||
| 					if d.is_finished_item or d.is_scrap_item: | ||||
| @ -346,6 +344,9 @@ class StockEntry(StockController): | ||||
| 			if cstr(d.s_warehouse) == cstr(d.t_warehouse) and not self.purpose == "Material Transfer for Manufacture": | ||||
| 				frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx)) | ||||
| 
 | ||||
| 			if not (d.s_warehouse or d.t_warehouse): | ||||
| 				frappe.throw(_("Atleast one warehouse is mandatory")) | ||||
| 
 | ||||
| 	def validate_work_order(self): | ||||
| 		if self.purpose in ("Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture"): | ||||
| 			# check if work order is entered | ||||
|  | ||||
| @ -278,6 +278,10 @@ def get_basic_details(args, item, overwrite_warehouse=True): | ||||
| 		else: | ||||
| 			args.uom = item.stock_uom | ||||
| 
 | ||||
| 	if (args.get("batch_no") and | ||||
| 		item.name != frappe.get_cached_value('Batch', args.get("batch_no"), 'item')): | ||||
| 		args['batch_no'] = '' | ||||
| 
 | ||||
| 	out = frappe._dict({ | ||||
| 		"item_code": item.name, | ||||
| 		"item_name": item.item_name, | ||||
|  | ||||
| @ -23,6 +23,7 @@ def execute(filters=None): | ||||
| 	conversion_factors = [] | ||||
| 	if opening_row: | ||||
| 		data.append(opening_row) | ||||
| 		conversion_factors.append(0) | ||||
| 
 | ||||
| 	actual_qty = stock_value = 0 | ||||
| 
 | ||||
|  | ||||
| @ -267,11 +267,15 @@ class TestServiceLevelAgreement(unittest.TestCase): | ||||
| 		) | ||||
| 		creation = datetime.datetime(2019, 3, 4, 12, 0) | ||||
| 		lead = make_lead(creation=creation, index=4) | ||||
| 		self.assertFalse(lead.service_level_agreement) | ||||
| 		applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement') | ||||
| 		self.assertFalse(applied_sla) | ||||
| 
 | ||||
| 		source = frappe.get_doc(doctype='Lead Source', source_name='Test Source') | ||||
| 		source.insert(ignore_if_duplicate=True) | ||||
| 		lead.source = "Test Source" | ||||
| 		lead.save() | ||||
| 		self.assertEqual(lead.service_level_agreement, lead_sla.name) | ||||
| 		applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement') | ||||
| 		self.assertEqual(applied_sla, lead_sla.name) | ||||
| 
 | ||||
| 	def tearDown(self): | ||||
| 		for d in frappe.get_all("Service Level Agreement"): | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user