Merge pull request #30515 from frappe/mergify/copy/version-13-hotfix/pr-30324

fix: multiple pos issues (copy #30324)
This commit is contained in:
Saqib Ansari
2022-04-01 12:11:06 +05:30
committed by GitHub
15 changed files with 404 additions and 69 deletions

View File

@@ -29,6 +29,8 @@ ignore =
B950,
W191,
E124, # closing bracket, irritating while writing QB code
E131, # continuation line unaligned for hanging indent
E123, # closing bracket does not match indentation of opening bracket's line
max-line-length = 200
exclude=.github/helper/semgrep_rules

View File

@@ -264,7 +264,6 @@
"print_hide": 1
},
{
"allow_on_submit": 1,
"default": "0",
"fieldname": "is_return",
"fieldtype": "Check",
@@ -1573,7 +1572,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2021-10-05 12:11:53.871828",
"modified": "2022-03-22 13:00:24.166684",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
@@ -1623,6 +1622,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,

View File

@@ -17,7 +17,11 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
)
from erpnext.accounts.party import get_due_date, get_party_account
from erpnext.stock.doctype.batch.batch import get_batch_qty, get_pos_reserved_batch_qty
from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
from erpnext.stock.doctype.serial_no.serial_no import (
get_delivered_serial_nos,
get_pos_reserved_serial_nos,
get_serial_nos,
)
class POSInvoice(SalesInvoice):
@@ -179,12 +183,7 @@ class POSInvoice(SalesInvoice):
)
def validate_delivered_serial_nos(self, item):
serial_nos = get_serial_nos(item.serial_no)
delivered_serial_nos = frappe.db.get_list(
"Serial No",
{"item_code": item.item_code, "name": ["in", serial_nos], "sales_invoice": ["is", "set"]},
pluck="name",
)
delivered_serial_nos = get_delivered_serial_nos(item.serial_no)
if delivered_serial_nos:
bold_delivered_serial_nos = frappe.bold(", ".join(delivered_serial_nos))
@@ -212,9 +211,16 @@ class POSInvoice(SalesInvoice):
frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
def validate_stock_availablility(self):
if self.is_return or self.docstatus != 1:
if self.is_return:
return
if self.docstatus == 0 and not frappe.db.get_value(
"POS Profile", self.pos_profile, "validate_stock_on_save"
):
return
allow_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
for d in self.get("items"):
is_service_item = not (frappe.db.get_value("Item", d.get("item_code"), "is_stock_item"))
if is_service_item:

View File

@@ -439,6 +439,7 @@ class TestPOSInvoice(unittest.TestCase):
)
si.get("items")[0].serial_no = serial_nos[0]
si.update_stock = 1
si.insert()
si.submit()
@@ -765,6 +766,76 @@ class TestPOSInvoice(unittest.TestCase):
pos_inv.delete()
pr.delete()
def test_delivered_serial_no_case(self):
from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
init_user_and_profile,
)
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
frappe.db.savepoint("before_test_delivered_serial_no_case")
try:
se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)
delivery_document_no = frappe.db.get_value("Serial No", serial_no, "delivery_document_no")
self.assertEquals(delivery_document_no, dn.name)
init_user_and_profile()
pos_inv = create_pos_invoice(
item_code="_Test Serialized Item With Series",
serial_no=serial_no,
qty=1,
rate=100,
do_not_submit=True,
)
self.assertRaises(frappe.ValidationError, pos_inv.submit)
finally:
frappe.db.rollback(save_point="before_test_delivered_serial_no_case")
frappe.set_user("Administrator")
def test_returned_serial_no_case(self):
from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
init_user_and_profile,
)
from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos
from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
frappe.db.savepoint("before_test_returned_serial_no_case")
try:
se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
init_user_and_profile()
pos_inv = create_pos_invoice(
item_code="_Test Serialized Item With Series",
serial_no=serial_no,
qty=1,
rate=100,
)
pos_return = make_sales_return(pos_inv.name)
pos_return.flags.ignore_validate = True
pos_return.insert()
pos_return.submit()
pos_reserved_serial_nos = get_pos_reserved_serial_nos(
{"item_code": "_Test Serialized Item With Series", "warehouse": "_Test Warehouse - _TC"}
)
self.assertTrue(serial_no not in pos_reserved_serial_nos)
finally:
frappe.db.rollback(save_point="before_test_returned_serial_no_case")
frappe.set_user("Administrator")
def create_pos_invoice(**args):
args = frappe._dict(args)

View File

@@ -5,7 +5,6 @@
import json
import frappe
import six
from frappe import _
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.model.document import Document
@@ -66,7 +65,9 @@ class POSInvoiceMergeLog(Document):
frappe.throw(msg)
def on_submit(self):
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
pos_invoice_docs = [
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
]
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
@@ -83,7 +84,9 @@ class POSInvoiceMergeLog(Document):
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
def on_cancel(self):
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
pos_invoice_docs = [
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
]
self.update_pos_invoices(pos_invoice_docs)
self.cancel_linked_invoices()
@@ -282,7 +285,14 @@ def get_all_unconsolidated_invoices():
pos_invoices = frappe.db.get_all(
"POS Invoice",
filters=filters,
fields=["name as pos_invoice", "posting_date", "grand_total", "customer"],
fields=[
"name as pos_invoice",
"posting_date",
"grand_total",
"customer",
"is_return",
"return_against",
],
)
return pos_invoices
@@ -327,19 +337,74 @@ def unconsolidate_pos_invoices(closing_entry):
cancel_merge_logs(merge_logs, closing_entry)
def split_invoices(invoices):
"""
Splits invoices into multiple groups
Use-case:
If a serial no is sold and later it is returned
then split the invoices such that the selling entry is merged first and then the return entry
"""
# Input
# invoices = [
# {'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0},
# {'pos_invoice': 'Invoice with SR#1', 'is_return': 1},
# {'pos_invoice': 'Invoice with SR#2', 'is_return': 0}
# ]
# Output
# _invoices = [
# [{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0}],
# [{'pos_invoice': 'Invoice with SR#1', 'is_return': 1}, {'pos_invoice': 'Invoice with SR#2', 'is_return': 0}],
# ]
_invoices = []
special_invoices = []
pos_return_docs = [
frappe.get_cached_doc("POS Invoice", d.pos_invoice)
for d in invoices
if d.is_return and d.return_against
]
for pos_invoice in pos_return_docs:
for item in pos_invoice.items:
if not item.serial_no:
continue
return_against_is_added = any(
d for d in _invoices if d.pos_invoice == pos_invoice.return_against
)
if return_against_is_added:
break
return_against_is_consolidated = (
frappe.db.get_value("POS Invoice", pos_invoice.return_against, "status", cache=True)
== "Consolidated"
)
if return_against_is_consolidated:
break
pos_invoice_row = [d for d in invoices if d.pos_invoice == pos_invoice.return_against]
_invoices.append(pos_invoice_row)
special_invoices.append(pos_invoice.return_against)
break
_invoices.append([d for d in invoices if d.pos_invoice not in special_invoices])
return _invoices
def create_merge_logs(invoice_by_customer, closing_entry=None):
try:
for customer, invoices in six.iteritems(invoice_by_customer):
merge_log = frappe.new_doc("POS Invoice Merge Log")
merge_log.posting_date = (
getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
)
merge_log.customer = customer
merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
for customer, invoices in invoice_by_customer.items():
for _invoices in split_invoices(invoices):
merge_log = frappe.new_doc("POS Invoice Merge Log")
merge_log.posting_date = (
getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
)
merge_log.customer = customer
merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
merge_log.set("pos_invoices", invoices)
merge_log.save(ignore_permissions=True)
merge_log.submit()
merge_log.set("pos_invoices", _invoices)
merge_log.save(ignore_permissions=True)
merge_log.submit()
if closing_entry:
closing_entry.set_status(update=True, status="Submitted")

View File

@@ -391,3 +391,61 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabPOS Profile`")
frappe.db.sql("delete from `tabPOS Invoice`")
def test_serial_no_case_1(self):
"""
Create a POS Invoice with serial no
Create a Return Invoice with serial no
Create a POS Invoice with serial no again
Consolidate the invoices
The first POS Invoice should be consolidated with a separate single Merge Log
The second and third POS Invoice should be consolidated with a single Merge Log
"""
from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
frappe.db.sql("delete from `tabPOS Invoice`")
try:
se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
init_user_and_profile()
pos_inv = create_pos_invoice(
item_code="_Test Serialized Item With Series",
serial_no=serial_no,
qty=1,
rate=100,
do_not_submit=1,
)
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv.submit()
pos_inv_cn = make_sales_return(pos_inv.name)
pos_inv_cn.paid_amount = -100
pos_inv_cn.submit()
pos_inv2 = create_pos_invoice(
item_code="_Test Serialized Item With Series",
serial_no=serial_no,
qty=1,
rate=100,
do_not_submit=1,
)
pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
pos_inv2.submit()
consolidate_pos_invoices()
pos_inv.load_from_db()
pos_inv2.load_from_db()
self.assertNotEqual(pos_inv.consolidated_invoice, pos_inv2.consolidated_invoice)
finally:
frappe.set_user("Administrator")
frappe.db.sql("delete from `tabPOS Profile`")
frappe.db.sql("delete from `tabPOS Invoice`")

View File

@@ -9,7 +9,9 @@
"posting_date",
"column_break_3",
"customer",
"grand_total"
"grand_total",
"is_return",
"return_against"
],
"fields": [
{
@@ -48,11 +50,27 @@
"in_list_view": 1,
"label": "Amount",
"reqd": 1
},
{
"default": "0",
"fetch_from": "pos_invoice.is_return",
"fieldname": "is_return",
"fieldtype": "Check",
"label": "Is Return",
"read_only": 1
},
{
"fetch_from": "pos_invoice.return_against",
"fieldname": "return_against",
"fieldtype": "Link",
"label": "Return Against",
"options": "POS Invoice",
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-05-29 15:08:42.194979",
"modified": "2022-03-24 13:32:02.366257",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Reference",
@@ -61,5 +79,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -22,6 +22,7 @@
"hide_images",
"hide_unavailable_items",
"auto_add_item_to_cart",
"validate_stock_on_save",
"column_break_16",
"update_stock",
"ignore_pricing_rule",
@@ -351,6 +352,12 @@
{
"fieldname": "column_break_25",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "validate_stock_on_save",
"fieldtype": "Check",
"label": "Validate Stock on Save"
}
],
"icon": "icon-cog",
@@ -378,10 +385,11 @@
"link_fieldname": "pos_profile"
}
],
"modified": "2021-10-14 14:17:00.469298",
"modified": "2022-03-21 13:29:28.480533",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
"naming_rule": "Set by user",
"owner": "Administrator",
"permissions": [
{
@@ -404,5 +412,6 @@
}
],
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": []
}

View File

@@ -357,3 +357,4 @@ erpnext.patches.v13_0.rename_non_profit_fields
erpnext.patches.v13_0.enable_ksa_vat_docs #1
erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
erpnext.patches.v13_0.set_return_against_in_pos_invoice_references

View File

@@ -0,0 +1,38 @@
import frappe
def execute():
"""
Fetch and Set is_return & return_against from POS Invoice in POS Invoice References table.
"""
POSClosingEntry = frappe.qb.DocType("POS Closing Entry")
open_pos_closing_entries = (
frappe.qb.from_(POSClosingEntry)
.select(POSClosingEntry.name)
.where(POSClosingEntry.docstatus == 0)
.run()
)
if open_pos_closing_entries:
open_pos_closing_entries = [d[0] for d in open_pos_closing_entries]
if not open_pos_closing_entries:
return
POSInvoiceReference = frappe.qb.DocType("POS Invoice Reference")
POSInvoice = frappe.qb.DocType("POS Invoice")
pos_invoice_references = (
frappe.qb.from_(POSInvoiceReference)
.join(POSInvoice)
.on(POSInvoiceReference.pos_invoice == POSInvoice.name)
.select(POSInvoiceReference.name, POSInvoice.is_return, POSInvoice.return_against)
.where(POSInvoiceReference.parent.isin(open_pos_closing_entries))
.run(as_dict=True)
)
for row in pos_invoice_references:
frappe.db.set_value("POS Invoice Reference", row.name, "is_return", row.is_return)
if row.is_return:
frappe.db.set_value("POS Invoice Reference", row.name, "return_against", row.return_against)
else:
frappe.db.set_value("POS Invoice Reference", row.name, "return_against", None)

View File

@@ -8,7 +8,7 @@ import frappe
from frappe.utils.nestedset import get_root_of
from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability
from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes, get_item_groups
def search_by_term(search_term, warehouse, price_list):
@@ -324,3 +324,17 @@ def set_customer_info(fieldname, customer, value=""):
contact_doc.set("phone_nos", [{"phone": value, "is_primary_mobile_no": 1}])
frappe.db.set_value("Customer", customer, "mobile_no", value)
contact_doc.save()
@frappe.whitelist()
def get_pos_profile_data(pos_profile):
pos_profile = frappe.get_doc("POS Profile", pos_profile)
pos_profile = pos_profile.as_dict()
_customer_groups_with_children = []
for row in pos_profile.customer_groups:
children = get_child_nodes("Customer Group", row.customer_group)
_customer_groups_with_children.extend(children)
pos_profile.customer_groups = _customer_groups_with_children
return pos_profile

View File

@@ -119,10 +119,15 @@ erpnext.PointOfSale.Controller = class {
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
});
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
Object.assign(this.settings, profile);
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
this.make_app();
frappe.call({
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_pos_profile_data",
args: { "pos_profile": this.pos_profile },
callback: (res) => {
const profile = res.message;
Object.assign(this.settings, profile);
this.settings.customer_groups = profile.customer_groups.map(group => group.name);
this.make_app();
}
});
}
@@ -555,7 +560,7 @@ erpnext.PointOfSale.Controller = class {
if (this.item_details.$component.is(':visible'))
this.edit_item_details_of(item_row);
if (this.check_serial_batch_selection_needed(item_row))
if (this.check_serial_batch_selection_needed(item_row) && !this.item_details.$component.is(':visible'))
this.edit_item_details_of(item_row);
}
@@ -704,7 +709,7 @@ erpnext.PointOfSale.Controller = class {
frappe.dom.freeze();
const { doctype, name, current_item } = this.item_details;
frappe.model.set_value(doctype, name, 'qty', 0)
return frappe.model.set_value(doctype, name, 'qty', 0)
.then(() => {
frappe.model.clear_doc(doctype, name);
this.update_cart_html(current_item, true);
@@ -715,7 +720,14 @@ erpnext.PointOfSale.Controller = class {
}
async save_and_checkout() {
this.frm.is_dirty() && await this.frm.save();
this.payment.checkout();
if (this.frm.is_dirty()) {
// only move to payment section if save is successful
frappe.route_hooks.after_save = () => this.payment.checkout();
return this.frm.save(
null, null, null, () => this.cart.toggle_checkout_btn(true) // show checkout button on error
);
} else {
this.payment.checkout();
}
}
};

View File

@@ -60,12 +60,18 @@ erpnext.PointOfSale.ItemDetails = class {
return item && item.name == this.current_item.name;
}
toggle_item_details_section(item) {
async toggle_item_details_section(item) {
const current_item_changed = !this.compare_with_current_item(item);
// if item is null or highlighted cart item is clicked twice
const hide_item_details = !Boolean(item) || !current_item_changed;
if ((!hide_item_details && current_item_changed) || hide_item_details) {
// if item details is being closed OR if item details is opened but item is changed
// in both cases, if the current item is a serialized item, then validate and remove the item
await this.validate_serial_batch_item();
}
this.events.toggle_item_selector(!hide_item_details);
this.toggle_component(!hide_item_details);
@@ -83,7 +89,6 @@ erpnext.PointOfSale.ItemDetails = class {
this.render_form(item);
this.events.highlight_cart_item(item);
} else {
this.validate_serial_batch_item();
this.current_item = {};
}
}
@@ -103,11 +108,11 @@ erpnext.PointOfSale.ItemDetails = class {
(serialized && batched && (no_batch_selected || no_serial_selected))) {
frappe.show_alert({
message: __("Item will be removed since no serial / batch no selected."),
message: __("Item is removed since no serial / batch no selected."),
indicator: 'orange'
});
frappe.utils.play_sound("cancel");
this.events.remove_item_from_cart();
return this.events.remove_item_from_cart();
}
}

View File

@@ -170,20 +170,24 @@ erpnext.PointOfSale.Payment = class {
});
frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => {
if (!frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
frappe.run_serially([
() => frm.doc.ignore_pricing_rule=1,
() => frm.trigger('ignore_pricing_rule'),
() => frm.doc.ignore_pricing_rule=0,
() => frm.trigger('apply_pricing_rule'),
() => frm.save(),
() => this.update_totals_section(frm.doc)
]);
} else if (frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
frappe.show_alert({
message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."),
indicator: "orange"
});
if (frm.doc.coupon_code && !frm.applying_pos_coupon_code) {
if (!frm.doc.ignore_pricing_rule) {
frm.applying_pos_coupon_code = true;
frappe.run_serially([
() => frm.doc.ignore_pricing_rule=1,
() => frm.trigger('ignore_pricing_rule'),
() => frm.doc.ignore_pricing_rule=0,
() => frm.trigger('apply_pricing_rule'),
() => frm.save(),
() => this.update_totals_section(frm.doc),
() => (frm.applying_pos_coupon_code = false)
]);
} else if (frm.doc.ignore_pricing_rule) {
frappe.show_alert({
message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."),
indicator: "orange"
});
}
}
});

View File

@@ -820,29 +820,60 @@ def auto_fetch_serial_number(
return sorted([d.get("name") for d in serial_numbers])
def get_delivered_serial_nos(serial_nos):
"""
Returns serial numbers that delivered from the list of serial numbers
"""
from frappe.query_builder.functions import Coalesce
SerialNo = frappe.qb.DocType("Serial No")
serial_nos = get_serial_nos(serial_nos)
query = (
frappe.qb.select(SerialNo.name)
.from_(SerialNo)
.where((SerialNo.name.isin(serial_nos)) & (Coalesce(SerialNo.delivery_document_type, "") != ""))
)
result = query.run()
if result and len(result) > 0:
delivered_serial_nos = [row[0] for row in result]
return delivered_serial_nos
@frappe.whitelist()
def get_pos_reserved_serial_nos(filters):
if isinstance(filters, str):
filters = json.loads(filters)
pos_transacted_sr_nos = frappe.db.sql(
"""select item.serial_no as serial_no
from `tabPOS Invoice` p, `tabPOS Invoice Item` item
where p.name = item.parent
and p.consolidated_invoice is NULL
and p.docstatus = 1
and item.docstatus = 1
and item.item_code = %(item_code)s
and item.warehouse = %(warehouse)s
and item.serial_no is NOT NULL and item.serial_no != ''
""",
filters,
as_dict=1,
POSInvoice = frappe.qb.DocType("POS Invoice")
POSInvoiceItem = frappe.qb.DocType("POS Invoice Item")
query = (
frappe.qb.from_(POSInvoice)
.from_(POSInvoiceItem)
.select(POSInvoice.is_return, POSInvoiceItem.serial_no)
.where(
(POSInvoice.name == POSInvoiceItem.parent)
& (POSInvoice.docstatus == 1)
& (POSInvoiceItem.docstatus == 1)
& (POSInvoiceItem.item_code == filters.get("item_code"))
& (POSInvoiceItem.warehouse == filters.get("warehouse"))
& (POSInvoiceItem.serial_no.isnotnull())
& (POSInvoiceItem.serial_no != "")
)
)
pos_transacted_sr_nos = query.run(as_dict=True)
reserved_sr_nos = []
returned_sr_nos = []
for d in pos_transacted_sr_nos:
reserved_sr_nos += get_serial_nos(d.serial_no)
if d.is_return == 0:
reserved_sr_nos += get_serial_nos(d.serial_no)
elif d.is_return == 1:
returned_sr_nos += get_serial_nos(d.serial_no)
for sr_no in returned_sr_nos:
reserved_sr_nos.remove(sr_no)
return reserved_sr_nos