@@ -316,4 +326,4 @@ erpnext.PointOfSale.ItemSelector = class {
toggle_component(show) {
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
}
-};
\ No newline at end of file
+};
From 18ad15ed160ce209cea7b6da7cc6fc07fb3ee152 Mon Sep 17 00:00:00 2001
From: Asharam Seervi
Date: Tue, 4 May 2021 19:28:07 +0530
Subject: [PATCH 067/115] fix: designation insufficient permission on lead
doctype. (#25331)
---
erpnext/hr/doctype/designation/designation.json | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/erpnext/hr/doctype/designation/designation.json b/erpnext/hr/doctype/designation/designation.json
index 4c3888be4a..bab6b90d1a 100644
--- a/erpnext/hr/doctype/designation/designation.json
+++ b/erpnext/hr/doctype/designation/designation.json
@@ -182,6 +182,10 @@
"share": 1,
"submit": 0,
"write": 1
+ },
+ {
+ "read": 1,
+ "role": "Sales User"
}
],
"quick_entry": 1,
@@ -191,4 +195,4 @@
"track_changes": 0,
"track_seen": 0,
"track_views": 0
-}
\ No newline at end of file
+}
From 04923d6a65637e7e6c50db826b46e92bbb491c2e Mon Sep 17 00:00:00 2001
From: Deepesh Garg
Date: Tue, 4 May 2021 21:01:12 +0530
Subject: [PATCH 068/115] fix: function call to update payment schedule labels
---
erpnext/public/js/controllers/transaction.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index f91b432a39..d218a5ee5f 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1384,7 +1384,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
company_currency, "payment_schedule");
this.frm.set_currency_labels(["payment_amount", "outstanding", "paid_amount"],
this.frm.doc.currency, "payment_schedule");
-
+
var schedule_grid = this.frm.fields_dict["payment_schedule"].grid;
$.each(["base_payment_amount", "base_outstanding", "base_paid_amount"], function(i, fname) {
if (frappe.meta.get_docfield(schedule_grid.doctype, fname))
@@ -2034,7 +2034,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(r.message && !r.exc) {
me.frm.set_value("payment_schedule", r.message);
const company_currency = me.get_company_currency();
- this.update_payment_schedule_grid_labels(company_currency);
+ me.update_payment_schedule_grid_labels(company_currency);
}
}
})
From 2a20a03c28c32944b5a5726241906010d42456af Mon Sep 17 00:00:00 2001
From: 18alantom <2.alan.tom@gmail.com>
Date: Wed, 5 May 2021 11:59:15 +0530
Subject: [PATCH 069/115] fix: check for None in item.schedule_date before
setting
---
erpnext/controllers/buying_controller.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index b686dc026c..3f2d3390c0 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -838,9 +838,10 @@ class BuyingController(StockController):
if not self.get("items"):
return
- earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
- if earliest_schedule_date:
- self.schedule_date = earliest_schedule_date
+ if any(d.schedule_date for d in self.get("items")):
+ # Select earliest schedule_date.
+ self.schedule_date = min(d.schedule_date for d in self.get("items")
+ if d.schedule_date is not None)
if self.schedule_date:
for d in self.get('items'):
From 1bb7bb74adb4b07bd2efc6e2fb641ed84a262fae Mon Sep 17 00:00:00 2001
From: Deepesh Garg
Date: Wed, 5 May 2021 12:19:57 +0530
Subject: [PATCH 070/115] fix: Check if payment schedule exits before updating
label
---
erpnext/public/js/controllers/transaction.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index d218a5ee5f..10c802c6f0 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1379,7 +1379,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
update_payment_schedule_grid_labels: function(company_currency) {
const me = this;
- if (this.frm.fields_dict["payment_schedule"]) {
+ if (this.frm.doc.payment_schedule.length > 0) {
this.frm.set_currency_labels(["base_payment_amount", "base_outstanding", "base_paid_amount"],
company_currency, "payment_schedule");
this.frm.set_currency_labels(["payment_amount", "outstanding", "paid_amount"],
From 136eb30081b7b74d3b500acf6731696b4692da8e Mon Sep 17 00:00:00 2001
From: 18alantom <2.alan.tom@gmail.com>
Date: Wed, 5 May 2021 12:26:29 +0530
Subject: [PATCH 071/115] fix: use get_serial_nos for splitting
---
erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4461f29fe3..4de877353a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1111,7 +1111,7 @@ class SalesInvoice(SellingController):
if not item.serial_no:
continue
- for serial_no in item.serial_no.split("\n"):
+ for serial_no in get_serial_nos(item.serial_no):
if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
From ffea9d4126c9ab544ac7a659874b9f41254404ec Mon Sep 17 00:00:00 2001
From: Deepesh Garg
Date: Wed, 5 May 2021 12:28:40 +0530
Subject: [PATCH 072/115] fix: Check if payment schedule exists
---
erpnext/public/js/controllers/transaction.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 10c802c6f0..0af8da77a0 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1379,7 +1379,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
update_payment_schedule_grid_labels: function(company_currency) {
const me = this;
- if (this.frm.doc.payment_schedule.length > 0) {
+ if (this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length > 0) {
this.frm.set_currency_labels(["base_payment_amount", "base_outstanding", "base_paid_amount"],
company_currency, "payment_schedule");
this.frm.set_currency_labels(["payment_amount", "outstanding", "paid_amount"],
From 85b675a554cfcdf78cfa5255e0a12e06e7e02e44 Mon Sep 17 00:00:00 2001
From: Deepesh Garg
Date: Wed, 5 May 2021 20:57:31 +0530
Subject: [PATCH 073/115] fix: Invoices not fetch during payment reconciliation
---
.../doctype/payment_reconciliation/payment_reconciliation.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index cf6ec18f3b..6635128f9e 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -114,7 +114,7 @@ class PaymentReconciliation(Document):
'party_type': self.party_type,
'voucher_type': voucher_type,
'account': self.receivable_payable_account
- }, as_dict=1, debug=1)
+ }, as_dict=1)
def add_payment_entries(self, entries):
self.set('payments', [])
From bb3e5d00f44869132d869fbead5046ba1d3ddfc6 Mon Sep 17 00:00:00 2001
From: Rohit Waghchaure
Date: Sat, 24 Apr 2021 17:28:33 +0530
Subject: [PATCH 074/115] fix: allow to receive same serial numbers multiple
times
---
.../purchase_receipt/test_purchase_receipt.py | 83 +++++++++++++------
erpnext/stock/doctype/serial_no/serial_no.py | 18 +---
.../stock/report/serial_no_ledger/__init__.py | 0
.../serial_no_ledger/serial_no_ledger.js | 52 ++++++++++++
.../serial_no_ledger/serial_no_ledger.json | 33 ++++++++
.../serial_no_ledger/serial_no_ledger.py | 53 ++++++++++++
erpnext/stock/stock_ledger.py | 53 ++++++++++--
7 files changed, 247 insertions(+), 45 deletions(-)
create mode 100644 erpnext/stock/report/serial_no_ledger/__init__.py
create mode 100644 erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
create mode 100644 erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
create mode 100644 erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 16eea24f84..f9b2c1d787 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -13,8 +13,9 @@ from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import make_item
from six import iteritems
+from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
class TestPurchaseReceipt(unittest.TestCase):
def setUp(self):
@@ -144,6 +145,62 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
+ def test_duplicate_serial_nos(self):
+ from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+ item = frappe.db.exists("Item", {'item_name': 'Test Serialized Item 123'})
+ if not item:
+ item = create_item("Test Serialized Item 123")
+ item.has_serial_no = 1
+ item.serial_no_series = "TSI123-.####"
+ item.save()
+ else:
+ item = frappe.get_doc("Item", {'item_name': 'Test Serialized Item 123'})
+
+ # First make purchase receipt
+ pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500)
+ pr.load_from_db()
+
+ serial_nos = frappe.db.get_value('Stock Ledger Entry',
+ {'voucher_type': 'Purchase Receipt', 'voucher_no': pr.name, 'item_code': item.name}, 'serial_no')
+
+ serial_nos = get_serial_nos(serial_nos)
+
+ self.assertEquals(get_serial_nos(pr.items[0].serial_no), serial_nos)
+
+ # Then tried to receive same serial nos in difference company
+ pr_different_company = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+ serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+ warehouse = 'Stores - _TC1')
+
+ self.assertRaises(SerialNoDuplicateError, pr_different_company.submit)
+
+ # Then made delivery note to remove the serial nos from stock
+ dn = create_delivery_note(item_code=item.name, qty=2, rate = 1500, serial_no='\n'.join(serial_nos))
+ dn.load_from_db()
+ self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos)
+
+ posting_date = add_days(today(), -3)
+
+ # Try to receive same serial nos again in the same company with backdated.
+ pr1 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+ posting_date=posting_date, serial_no='\n'.join(serial_nos), do_not_submit=True)
+
+ self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit)
+
+ # Try to receive same serial nos with different company with backdated.
+ pr2 = make_purchase_receipt(item_code=item.name, qty=2, rate=500,
+ posting_date=posting_date, serial_no='\n'.join(serial_nos), company='_Test Company 1', do_not_submit=True,
+ warehouse = 'Stores - _TC1')
+
+ self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit)
+
+ # Receive the same serial nos after the delivery note posting date and time
+ make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no='\n'.join(serial_nos))
+
+ # Raise the error for backdated deliver note entry cancel
+ self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel)
+
def test_purchase_receipt_gl_entry(self):
pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
@@ -562,30 +619,6 @@ class TestPurchaseReceipt(unittest.TestCase):
new_pr_doc.cancel()
- def test_not_accept_duplicate_serial_no(self):
- from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
-
- item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0, "has_batch_no": 0})
- if not item_code:
- item = make_item("Test Serial Item 1", dict(has_serial_no=1, has_batch_no=0))
- item_code = item.name
-
- serial_no = random_string(5)
- pr1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
- dn = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no)
-
- pr2 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True)
- self.assertRaises(SerialNoDuplicateError, pr2.submit)
-
- se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1,
- serial_no=serial_no, basic_rate=100, do_not_submit=True)
- se.submit()
-
- se.cancel()
- dn.cancel()
- pr1.cancel()
-
def test_auto_asset_creation(self):
asset_item = "Test Asset Item"
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index c02dd2e518..5ecc9f8140 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -243,7 +243,7 @@ def validate_serial_no(sle, item_det):
if frappe.db.exists("Serial No", serial_no):
sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
"delivery_document_no", "delivery_document_type", "warehouse", "purchase_document_type",
- "purchase_document_no", "company"], as_dict=1)
+ "purchase_document_no", "company", "status"], as_dict=1)
if sr.item_code!=sle.item_code:
if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -266,6 +266,9 @@ def validate_serial_no(sle, item_det):
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
sle.warehouse), SerialNoWarehouseError)
+ if not sr.purchase_document_no:
+ frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
+
if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
if sr.batch_no and sr.batch_no != sle.batch_no:
@@ -382,19 +385,6 @@ def has_serial_no_exists(sn, sle):
if sn.company != sle.company:
return False
- status = False
- if sn.purchase_document_no:
- if (sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and
- sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]):
- status = True
-
- # If status is receipt then system will allow to in-ward the delivered serial no
- if (status and sle.voucher_type == "Stock Entry" and frappe.db.get_value("Stock Entry",
- sle.voucher_no, "purpose") in ("Material Receipt", "Material Transfer")):
- status = False
-
- return status
-
def allow_serial_nos_with_different_item(sle_serial_no, sle):
"""
Allows same serial nos for raw materials and finished goods
diff --git a/erpnext/stock/report/serial_no_ledger/__init__.py b/erpnext/stock/report/serial_no_ledger/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
new file mode 100644
index 0000000000..616312e311
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Serial No Ledger"] = {
+ "filters": [
+ {
+ 'label': __('Item Code'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'item_code',
+ 'reqd': 1,
+ 'options': 'Item',
+ get_query: function() {
+ return {
+ filters: {
+ 'has_serial_no': 1
+ }
+ }
+ }
+ },
+ {
+ 'label': __('Serial No'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'serial_no',
+ 'options': 'Serial No',
+ 'reqd': 1
+ },
+ {
+ 'label': __('Warehouse'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'warehouse',
+ 'options': 'Warehouse',
+ get_query: function() {
+ let company = frappe.query_report.get_filter_value('company');
+
+ if (company) {
+ return {
+ filters: {
+ 'company': company
+ }
+ }
+ }
+ }
+ },
+ {
+ 'label': __('As On Date'),
+ 'fieldtype': 'Date',
+ 'fieldname': 'posting_date',
+ 'default': frappe.datetime.get_today()
+ },
+ ]
+};
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
new file mode 100644
index 0000000000..e20e74c78b
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-04-20 13:32:41.523219",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2021-04-20 13:33:19.015829",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Serial No Ledger",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Stock Ledger Entry",
+ "report_name": "Serial No Ledger",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Stock User"
+ },
+ {
+ "role": "Purchase User"
+ },
+ {
+ "role": "Sales User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
new file mode 100644
index 0000000000..c3339fd341
--- /dev/null
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe import _
+from erpnext.stock.stock_ledger import get_stock_ledger_entries
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+def execute(filters=None):
+ columns = get_columns(filters)
+ data = get_data(filters)
+ return columns, data
+
+def get_columns(filters):
+ columns = [{
+ 'label': _('Posting Date'),
+ 'fieldtype': 'Date',
+ 'fieldname': 'posting_date'
+ }, {
+ 'label': _('Posting Time'),
+ 'fieldtype': 'Time',
+ 'fieldname': 'posting_time'
+ }, {
+ 'label': _('Voucher Type'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'voucher_type',
+ 'options': 'DocType',
+ 'width': 220
+ }, {
+ 'label': _('Voucher No'),
+ 'fieldtype': 'Dynamic Link',
+ 'fieldname': 'voucher_no',
+ 'options': 'voucher_type',
+ 'width': 220
+ }, {
+ 'label': _('Company'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'company',
+ 'options': 'Company',
+ 'width': 220
+ }, {
+ 'label': _('Warehouse'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'warehouse',
+ 'options': 'Warehouse',
+ 'width': 220
+ }]
+
+ return columns
+
+def get_data(filters):
+ return get_stock_ledger_entries(filters, '<=', order="asc") or []
+
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index bbfcb7ad7d..9729987d2d 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -2,9 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, erpnext
+import frappe
+import erpnext
+import copy
from frappe import _
-from frappe.utils import cint, flt, cstr, now, now_datetime
+from frappe.utils import cint, flt, cstr, now, get_link_to_form
from frappe.model.meta import get_field_precision
from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
from erpnext.stock.utils import get_bin
@@ -13,6 +15,8 @@ from six import iteritems
# future reposting
class NegativeStockError(frappe.ValidationError): pass
+class SerialNoExistsInFutureTransaction(frappe.ValidationError):
+ pass
_exceptions = frappe.local('stockledger_exceptions')
# _exceptions = []
@@ -27,6 +31,9 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
for sle in sl_entries:
+ if sle.serial_no:
+ validate_serial_no(sle)
+
if cancel:
sle['actual_qty'] = -flt(sle.get('actual_qty'))
@@ -46,6 +53,30 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
args = sle_doc.as_dict()
update_bin(args, allow_negative_stock, via_landed_cost_voucher)
+def validate_serial_no(sle):
+ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ for sn in get_serial_nos(sle.serial_no):
+ args = copy.deepcopy(sle)
+ args.serial_no = sn
+ args.warehouse = ''
+
+ vouchers = []
+ for row in get_stock_ledger_entries(args, '>'):
+ voucher_type = frappe.bold(row.voucher_type)
+ voucher_no = frappe.bold(get_link_to_form(row.voucher_type, row.voucher_no))
+ vouchers.append(f'{voucher_type} {voucher_no}')
+
+ if vouchers:
+ serial_no = frappe.bold(sn)
+ msg = (f'''The serial no {serial_no} has been used in the future transactions so you need to cancel them first.
+ The list of the transactions are as below.''' + '
')
+
+ msg += '
'.join(vouchers)
+ msg += '
'
+
+ title = 'Cannot Submit' if not sle.get('is_cancelled') else 'Cannot Cancel'
+ frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction)
+
def validate_cancellation(args):
if args[0].get("is_cancelled"):
repost_entry = frappe.db.get_value("Repost Item Valuation", {
@@ -718,7 +749,17 @@ def get_stock_ledger_entries(previous_sle, operator=None,
conditions += " and " + previous_sle.get("warehouse_condition")
if check_serial_no and previous_sle.get("serial_no"):
- conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+ # conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no"))))
+ serial_no = previous_sle.get("serial_no")
+ conditions += (""" and
+ (
+ serial_no = {0}
+ or serial_no like {1}
+ or serial_no like {2}
+ or serial_no like {3}
+ )
+ """).format(frappe.db.escape(serial_no), frappe.db.escape('{}\n%'.format(serial_no)),
+ frappe.db.escape('%\n{}'.format(serial_no)), frappe.db.escape('%\n{}\n%'.format(serial_no)))
if not previous_sle.get("posting_date"):
previous_sle["posting_date"] = "1900-01-01"
@@ -793,12 +834,12 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
and cint(erpnext.is_perpetual_inventory_enabled(company)):
frappe.local.message_log = []
- form_link = frappe.utils.get_link_to_form("Item", item_code)
+ form_link = get_link_to_form("Item", item_code)
message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
- message += "
" + _(" Here are the options to proceed:")
+ message += "
" + _("Here are the options to proceed:")
solutions = "
" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "
"
- solutions += "
" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "
"
+ solutions += "
" + _("If not, you can Cancel / Submit this entry") + " {0} ".format(frappe.bold("after")) + _("performing either one below:") + "
"
sub_solutions = "
" + _("Create an incoming stock transaction for the Item.") + "
"
sub_solutions += "
" + _("Mention Valuation Rate in the Item master.") + "
"
msg = message + solutions + sub_solutions + ""
From d502f763197b9d3891c4c2e498c132a87d35ee19 Mon Sep 17 00:00:00 2001
From: Saqib
Date: Thu, 6 May 2021 16:10:55 +0530
Subject: [PATCH 075/115] feat(e-invoicing): e-way bill validity field (#25555)
---
erpnext/patches.txt | 1 +
.../patches/v12_0/add_ewaybill_validity_field.py | 16 ++++++++++++++++
erpnext/regional/india/e_invoice/utils.py | 12 ++++++++----
erpnext/regional/india/setup.py | 3 +++
4 files changed, 28 insertions(+), 4 deletions(-)
create mode 100644 erpnext/patches/v12_0/add_ewaybill_validity_field.py
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 23f9fd8ecb..7faaf26158 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -774,5 +774,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
diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
new file mode 100644
index 0000000000..87d98f1a56
--- /dev/null
+++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
@@ -0,0 +1,16 @@
+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)
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 699441be7e..b4e7a8889e 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -71,13 +71,14 @@ def validate_einvoice_fields(doc):
def raise_document_name_too_long_error():
title = _('Document ID Too Long')
- msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice, ')
- msg += _('document id {} exceed 16 letters. ').format(bold(_('should not')))
+ msg = _('As you have E-Invoicing enabled, to be able to generate IRN for this invoice')
+ msg += ', '
+ msg += _('document id {} exceed 16 letters.').format(bold(_('should not')))
msg += '