Merge pull request #25471 from rohitwaghchaure/allow-to-receipt-same-serial-no
fix: allow to receive same serial numbers multiple times
This commit is contained in:
commit
1726ce547c
@ -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.accounts.doctype.account.test_account import get_inventory_account
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from six import iteritems
|
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.warehouse.test_warehouse import create_warehouse
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
class TestPurchaseReceipt(unittest.TestCase):
|
class TestPurchaseReceipt(unittest.TestCase):
|
||||||
def setUp(self):
|
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_value('Batch', {'item': item.name, 'reference_name': pr.name}))
|
||||||
self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
|
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):
|
def test_purchase_receipt_gl_entry(self):
|
||||||
pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
|
pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
|
||||||
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
|
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
|
||||||
@ -562,30 +619,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
|
|
||||||
new_pr_doc.cancel()
|
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):
|
def test_auto_asset_creation(self):
|
||||||
asset_item = "Test Asset Item"
|
asset_item = "Test Asset Item"
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ def validate_serial_no(sle, item_det):
|
|||||||
if frappe.db.exists("Serial No", serial_no):
|
if frappe.db.exists("Serial No", serial_no):
|
||||||
sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
|
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",
|
"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 sr.item_code!=sle.item_code:
|
||||||
if not allow_serial_nos_with_different_item(serial_no, sle):
|
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,
|
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
|
||||||
sle.warehouse), SerialNoWarehouseError)
|
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 sle.voucher_type in ("Delivery Note", "Sales Invoice"):
|
||||||
|
|
||||||
if sr.batch_no and sr.batch_no != sle.batch_no:
|
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:
|
if sn.company != sle.company:
|
||||||
return False
|
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):
|
def allow_serial_nos_with_different_item(sle_serial_no, sle):
|
||||||
"""
|
"""
|
||||||
Allows same serial nos for raw materials and finished goods
|
Allows same serial nos for raw materials and finished goods
|
||||||
|
0
erpnext/stock/report/serial_no_ledger/__init__.py
Normal file
0
erpnext/stock/report/serial_no_ledger/__init__.py
Normal file
52
erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
Normal file
52
erpnext/stock/report/serial_no_ledger/serial_no_ledger.js
Normal file
@ -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()
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
33
erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
Normal file
33
erpnext/stock/report/serial_no_ledger/serial_no_ledger.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
53
erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
Normal file
53
erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
Normal file
@ -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 []
|
||||||
|
|
@ -2,9 +2,11 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe, erpnext
|
import frappe
|
||||||
|
import erpnext
|
||||||
|
import copy
|
||||||
from frappe import _
|
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 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_valuation_method, get_incoming_outgoing_rate_for_cancel
|
||||||
from erpnext.stock.utils import get_bin
|
from erpnext.stock.utils import get_bin
|
||||||
@ -13,6 +15,8 @@ from six import iteritems
|
|||||||
|
|
||||||
# future reposting
|
# future reposting
|
||||||
class NegativeStockError(frappe.ValidationError): pass
|
class NegativeStockError(frappe.ValidationError): pass
|
||||||
|
class SerialNoExistsInFutureTransaction(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
_exceptions = frappe.local('stockledger_exceptions')
|
_exceptions = frappe.local('stockledger_exceptions')
|
||||||
# _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'))
|
set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
||||||
|
|
||||||
for sle in sl_entries:
|
for sle in sl_entries:
|
||||||
|
if sle.serial_no:
|
||||||
|
validate_serial_no(sle)
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
sle['actual_qty'] = -flt(sle.get('actual_qty'))
|
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()
|
args = sle_doc.as_dict()
|
||||||
update_bin(args, allow_negative_stock, via_landed_cost_voucher)
|
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.''' + '<br><br><ul><li>')
|
||||||
|
|
||||||
|
msg += '</li><li>'.join(vouchers)
|
||||||
|
msg += '</li></ul>'
|
||||||
|
|
||||||
|
title = 'Cannot Submit' if not sle.get('is_cancelled') else 'Cannot Cancel'
|
||||||
|
frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction)
|
||||||
|
|
||||||
def validate_cancellation(args):
|
def validate_cancellation(args):
|
||||||
if args[0].get("is_cancelled"):
|
if args[0].get("is_cancelled"):
|
||||||
repost_entry = frappe.db.get_value("Repost Item Valuation", {
|
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")
|
conditions += " and " + previous_sle.get("warehouse_condition")
|
||||||
|
|
||||||
if check_serial_no and previous_sle.get("serial_no"):
|
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"):
|
if not previous_sle.get("posting_date"):
|
||||||
previous_sle["posting_date"] = "1900-01-01"
|
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 \
|
if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
|
||||||
and cint(erpnext.is_perpetual_inventory_enabled(company)):
|
and cint(erpnext.is_perpetual_inventory_enabled(company)):
|
||||||
frappe.local.message_log = []
|
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 = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
|
||||||
message += "<br><br>" + _(" Here are the options to proceed:")
|
message += "<br><br>" + _("Here are the options to proceed:")
|
||||||
solutions = "<li>" + _("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) + "</li>"
|
solutions = "<li>" + _("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) + "</li>"
|
||||||
solutions += "<li>" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "</li>"
|
solutions += "<li>" + _("If not, you can Cancel / Submit this entry") + " {0} ".format(frappe.bold("after")) + _("performing either one below:") + "</li>"
|
||||||
sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
|
sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
|
||||||
sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
|
sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
|
||||||
msg = message + solutions + sub_solutions + "</li>"
|
msg = message + solutions + sub_solutions + "</li>"
|
||||||
|
Loading…
Reference in New Issue
Block a user