Merge pull request #33715 from s-aga-r/fix-pick-list
fix: consider existing pick-list
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6744d16..698ffac 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -325,3 +325,4 @@
erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request
erpnext.patches.v14_0.update_entry_type_for_journal_entry
erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers
+erpnext.patches.v14_0.set_pick_list_status
diff --git a/erpnext/patches/v14_0/set_pick_list_status.py b/erpnext/patches/v14_0/set_pick_list_status.py
new file mode 100644
index 0000000..eea5745
--- /dev/null
+++ b/erpnext/patches/v14_0/set_pick_list_status.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: MIT. See LICENSE
+
+
+import frappe
+from pypika.terms import ExistsCriterion
+
+
+def execute():
+ pl = frappe.qb.DocType("Pick List")
+ se = frappe.qb.DocType("Stock Entry")
+ dn = frappe.qb.DocType("Delivery Note")
+
+ (
+ frappe.qb.update(pl).set(
+ pl.status,
+ (
+ frappe.qb.terms.Case()
+ .when(pl.docstatus == 0, "Draft")
+ .when(pl.docstatus == 2, "Cancelled")
+ .else_("Completed")
+ ),
+ )
+ ).run()
+
+ (
+ frappe.qb.update(pl)
+ .set(pl.status, "Open")
+ .where(
+ (
+ ExistsCriterion(
+ frappe.qb.from_(se).select(se.name).where((se.docstatus == 1) & (se.pick_list == pl.name))
+ )
+ | ExistsCriterion(
+ frappe.qb.from_(dn).select(dn.name).where((dn.docstatus == 1) & (dn.pick_list == pl.name))
+ )
+ ).negate()
+ & (pl.docstatus == 1)
+ )
+ ).run()
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index a1df764..9f9f5cb 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -228,6 +228,7 @@
def on_submit(self):
self.validate_packed_qty()
+ self.update_pick_list_status()
# Check for Approving Authority
frappe.get_doc("Authorization Control").validate_approving_authority(
@@ -313,6 +314,11 @@
if has_error:
raise frappe.ValidationError
+ def update_pick_list_status(self):
+ from erpnext.stock.doctype.pick_list.pick_list import update_pick_list_status
+
+ update_pick_list_status(self.pick_list)
+
def check_next_docstatus(self):
submit_rv = frappe.db.sql(
"""select t1.name
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index e1c3f0f..7259dc0 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -26,7 +26,8 @@
"locations",
"amended_from",
"print_settings_section",
- "group_same_items"
+ "group_same_items",
+ "status"
],
"fields": [
{
@@ -168,11 +169,26 @@
"fieldtype": "Data",
"label": "Customer Name",
"read_only": 1
+ },
+ {
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Draft\nOpen\nCompleted\nCancelled",
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1,
+ "reqd": 1,
+ "search_index": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2022-07-19 11:03:04.442174",
+ "modified": "2023-01-24 10:33:43.244476",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
@@ -244,4 +260,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 808f19e..bf3b5dd 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -11,7 +11,8 @@
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc
from frappe.query_builder import Case
-from frappe.query_builder.functions import IfNull, Locate, Sum
+from frappe.query_builder.custom import GROUP_CONCAT
+from frappe.query_builder.functions import Coalesce, IfNull, Locate, Replace, Sum
from frappe.utils import cint, floor, flt, today
from frappe.utils.nestedset import get_descendants_of
@@ -77,15 +78,32 @@
)
def on_submit(self):
+ self.update_status()
self.update_bundle_picked_qty()
self.update_reference_qty()
self.update_sales_order_picking_status()
def on_cancel(self):
+ self.update_status()
self.update_bundle_picked_qty()
self.update_reference_qty()
self.update_sales_order_picking_status()
+ def update_status(self, status=None, update_modified=True):
+ if not status:
+ if self.docstatus == 0:
+ status = "Draft"
+ elif self.docstatus == 1:
+ if self.status == "Draft":
+ status = "Open"
+ elif target_document_exists(self.name, self.purpose):
+ status = "Completed"
+ elif self.docstatus == 2:
+ status = "Cancelled"
+
+ if status:
+ frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified)
+
def update_reference_qty(self):
packed_items = []
so_items = []
@@ -162,6 +180,7 @@
def set_item_locations(self, save=False):
self.validate_for_qty()
items = self.aggregate_item_qty()
+ picked_items_details = self.get_picked_items_details(items)
self.item_location_map = frappe._dict()
from_warehouses = None
@@ -180,7 +199,11 @@
self.item_location_map.setdefault(
item_code,
get_available_item_locations(
- item_code, from_warehouses, self.item_count_map.get(item_code), self.company
+ item_code,
+ from_warehouses,
+ self.item_count_map.get(item_code),
+ self.company,
+ picked_item_details=picked_items_details.get(item_code),
),
)
@@ -309,6 +332,56 @@
already_picked + (picked_qty * (1 if self.docstatus == 1 else -1)),
)
+ def get_picked_items_details(self, items):
+ picked_items = frappe._dict()
+
+ if items:
+ pi = frappe.qb.DocType("Pick List")
+ pi_item = frappe.qb.DocType("Pick List Item")
+ query = (
+ frappe.qb.from_(pi)
+ .inner_join(pi_item)
+ .on(pi.name == pi_item.parent)
+ .select(
+ pi_item.item_code,
+ pi_item.warehouse,
+ pi_item.batch_no,
+ Sum(Case().when(pi_item.picked_qty > 0, pi_item.picked_qty).else_(pi_item.stock_qty)).as_(
+ "picked_qty"
+ ),
+ Replace(GROUP_CONCAT(pi_item.serial_no), ",", "\n").as_("serial_no"),
+ )
+ .where(
+ (pi_item.item_code.isin([x.item_code for x in items]))
+ & ((pi_item.picked_qty > 0) | (pi_item.stock_qty > 0))
+ & (pi.status != "Completed")
+ & (pi_item.docstatus != 2)
+ )
+ .groupby(
+ pi_item.item_code,
+ pi_item.warehouse,
+ pi_item.batch_no,
+ )
+ )
+
+ if self.name:
+ query = query.where(pi_item.parent != self.name)
+
+ items_data = query.run(as_dict=True)
+
+ for item_data in items_data:
+ key = (item_data.warehouse, item_data.batch_no) if item_data.batch_no else item_data.warehouse
+ serial_no = [x for x in item_data.serial_no.split("\n") if x] if item_data.serial_no else None
+ data = {"picked_qty": item_data.picked_qty}
+ if serial_no:
+ data["serial_no"] = serial_no
+ if item_data.item_code not in picked_items:
+ picked_items[item_data.item_code] = {key: data}
+ else:
+ picked_items[item_data.item_code][key] = data
+
+ return picked_items
+
def _get_product_bundles(self) -> Dict[str, str]:
# Dict[so_item_row: item_code]
product_bundles = {}
@@ -346,29 +419,30 @@
return int(flt(min(possible_bundles), precision or 6))
+def update_pick_list_status(pick_list):
+ if pick_list:
+ doc = frappe.get_doc("Pick List", pick_list)
+ doc.run_method("update_status")
+
+
def get_picked_items_qty(items) -> List[Dict]:
- return frappe.db.sql(
- f"""
- SELECT
- sales_order_item,
- item_code,
- sales_order,
- SUM(stock_qty) AS stock_qty,
- SUM(picked_qty) AS picked_qty
- FROM
- `tabPick List Item`
- WHERE
- sales_order_item IN (
- {", ".join(frappe.db.escape(d) for d in items)}
- )
- AND docstatus = 1
- GROUP BY
- sales_order_item,
- sales_order
- FOR UPDATE
- """,
- as_dict=1,
- )
+ pi_item = frappe.qb.DocType("Pick List Item")
+ return (
+ frappe.qb.from_(pi_item)
+ .select(
+ pi_item.sales_order_item,
+ pi_item.item_code,
+ pi_item.sales_order,
+ Sum(pi_item.stock_qty).as_("stock_qty"),
+ Sum(pi_item.picked_qty).as_("picked_qty"),
+ )
+ .where((pi_item.docstatus == 1) & (pi_item.sales_order_item.isin(items)))
+ .groupby(
+ pi_item.sales_order_item,
+ pi_item.sales_order,
+ )
+ .for_update()
+ ).run(as_dict=True)
def validate_item_locations(pick_list):
@@ -434,31 +508,38 @@
def get_available_item_locations(
- item_code, from_warehouses, required_qty, company, ignore_validation=False
+ item_code,
+ from_warehouses,
+ required_qty,
+ company,
+ ignore_validation=False,
+ picked_item_details=None,
):
locations = []
+ total_picked_qty = (
+ sum([v.get("picked_qty") for k, v in picked_item_details.items()]) if picked_item_details else 0
+ )
has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no")
has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no")
if has_batch_no and has_serial_no:
locations = get_available_item_locations_for_serial_and_batched_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty
)
elif has_serial_no:
locations = get_available_item_locations_for_serialized_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty
)
elif has_batch_no:
locations = get_available_item_locations_for_batched_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty
)
else:
locations = get_available_item_locations_for_other_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty
)
total_qty_available = sum(location.get("qty") for location in locations)
-
remaining_qty = required_qty - total_qty_available
if remaining_qty > 0 and not ignore_validation:
@@ -469,25 +550,60 @@
title=_("Insufficient Stock"),
)
+ if picked_item_details:
+ for location in list(locations):
+ key = (
+ (location["warehouse"], location["batch_no"])
+ if location.get("batch_no")
+ else location["warehouse"]
+ )
+
+ if key in picked_item_details:
+ picked_detail = picked_item_details[key]
+
+ if picked_detail.get("serial_no") and location.get("serial_no"):
+ location["serial_no"] = list(
+ set(location["serial_no"]).difference(set(picked_detail["serial_no"]))
+ )
+ location["qty"] = len(location["serial_no"])
+ else:
+ location["qty"] -= picked_detail.get("picked_qty")
+
+ if location["qty"] < 1:
+ locations.remove(location)
+
+ total_qty_available = sum(location.get("qty") for location in locations)
+ remaining_qty = required_qty - total_qty_available
+
+ if remaining_qty > 0 and not ignore_validation:
+ frappe.msgprint(
+ _("{0} units of Item {1} is picked in another Pick List.").format(
+ remaining_qty, frappe.get_desk_link("Item", item_code)
+ ),
+ title=_("Already Picked"),
+ )
+
return locations
def get_available_item_locations_for_serialized_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty=0
):
- filters = frappe._dict({"item_code": item_code, "company": company, "warehouse": ["!=", ""]})
+ sn = frappe.qb.DocType("Serial No")
+ query = (
+ frappe.qb.from_(sn)
+ .select(sn.name, sn.warehouse)
+ .where((sn.item_code == item_code) & (sn.company == company))
+ .orderby(sn.purchase_date)
+ .limit(cint(required_qty + total_picked_qty))
+ )
if from_warehouses:
- filters.warehouse = ["in", from_warehouses]
+ query = query.where(sn.warehouse.isin(from_warehouses))
+ else:
+ query = query.where(Coalesce(sn.warehouse, "") != "")
- serial_nos = frappe.get_all(
- "Serial No",
- fields=["name", "warehouse"],
- filters=filters,
- limit=required_qty,
- order_by="purchase_date",
- as_list=1,
- )
+ serial_nos = query.run(as_list=True)
warehouse_serial_nos_map = frappe._dict()
for serial_no, warehouse in serial_nos:
@@ -501,7 +617,7 @@
def get_available_item_locations_for_batched_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty=0
):
sle = frappe.qb.DocType("Stock Ledger Entry")
batch = frappe.qb.DocType("Batch")
@@ -521,6 +637,7 @@
.groupby(sle.warehouse, sle.batch_no, sle.item_code)
.having(Sum(sle.actual_qty) > 0)
.orderby(IfNull(batch.expiry_date, "2200-01-01"), batch.creation, sle.batch_no, sle.warehouse)
+ .limit(cint(required_qty + total_picked_qty))
)
if from_warehouses:
@@ -530,53 +647,58 @@
def get_available_item_locations_for_serial_and_batched_item(
- item_code, from_warehouses, required_qty, company
+ item_code, from_warehouses, required_qty, company, total_picked_qty=0
):
# Get batch nos by FIFO
locations = get_available_item_locations_for_batched_item(
item_code, from_warehouses, required_qty, company
)
- filters = frappe._dict(
- {"item_code": item_code, "company": company, "warehouse": ["!=", ""], "batch_no": ""}
- )
+ if locations:
+ sn = frappe.qb.DocType("Serial No")
+ conditions = (sn.item_code == item_code) & (sn.company == company)
- # Get Serial Nos by FIFO for Batch No
- for location in locations:
- filters.batch_no = location.batch_no
- filters.warehouse = location.warehouse
- location.qty = (
- required_qty if location.qty > required_qty else location.qty
- ) # if extra qty in batch
+ for location in locations:
+ location.qty = (
+ required_qty if location.qty > required_qty else location.qty
+ ) # if extra qty in batch
- serial_nos = frappe.get_list(
- "Serial No", fields=["name"], filters=filters, limit=location.qty, order_by="purchase_date"
- )
+ serial_nos = (
+ frappe.qb.from_(sn)
+ .select(sn.name)
+ .where(
+ (conditions) & (sn.batch_no == location.batch_no) & (sn.warehouse == location.warehouse)
+ )
+ .orderby(sn.purchase_date)
+ .limit(cint(location.qty + total_picked_qty))
+ ).run(as_dict=True)
- serial_nos = [sn.name for sn in serial_nos]
- location.serial_no = serial_nos
+ serial_nos = [sn.name for sn in serial_nos]
+ location.serial_no = serial_nos
+ location.qty = len(serial_nos)
return locations
-def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company):
- # gets all items available in different warehouses
- warehouses = [x.get("name") for x in frappe.get_list("Warehouse", {"company": company}, "name")]
-
- filters = frappe._dict(
- {"item_code": item_code, "warehouse": ["in", warehouses], "actual_qty": [">", 0]}
+def get_available_item_locations_for_other_item(
+ item_code, from_warehouses, required_qty, company, total_picked_qty=0
+):
+ bin = frappe.qb.DocType("Bin")
+ query = (
+ frappe.qb.from_(bin)
+ .select(bin.warehouse, bin.actual_qty.as_("qty"))
+ .where((bin.item_code == item_code) & (bin.actual_qty > 0))
+ .orderby(bin.creation)
+ .limit(cint(required_qty + total_picked_qty))
)
if from_warehouses:
- filters.warehouse = ["in", from_warehouses]
+ query = query.where(bin.warehouse.isin(from_warehouses))
+ else:
+ wh = frappe.qb.DocType("Warehouse")
+ query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company))
- item_locations = frappe.get_all(
- "Bin",
- fields=["warehouse", "actual_qty as qty"],
- filters=filters,
- limit=required_qty,
- order_by="creation",
- )
+ item_locations = query.run(as_dict=True)
return item_locations
diff --git a/erpnext/stock/doctype/pick_list/pick_list_list.js b/erpnext/stock/doctype/pick_list/pick_list_list.js
new file mode 100644
index 0000000..ad88b0a
--- /dev/null
+++ b/erpnext/stock/doctype/pick_list/pick_list_list.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.listview_settings['Pick List'] = {
+ get_indicator: function (doc) {
+ const status_colors = {
+ "Draft": "grey",
+ "Open": "orange",
+ "Completed": "green",
+ "Cancelled": "red",
+ };
+ return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
+ },
+};
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 43acdf0..1254fe3 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -414,6 +414,7 @@
pick_list.submit()
delivery_note = create_delivery_note(pick_list.name)
+ pick_list.load_from_db()
self.assertEqual(pick_list.locations[0].qty, delivery_note.items[0].qty)
self.assertEqual(pick_list.locations[1].qty, delivery_note.items[1].qty)
@@ -663,3 +664,147 @@
self.assertEqual(dn.items[0].rate, 42)
so.reload()
self.assertEqual(so.per_delivered, 100)
+
+ def test_pick_list_status(self):
+ warehouse = "_Test Warehouse - _TC"
+ item = make_item(properties={"maintain_stock": 1}).name
+ make_stock_entry(item=item, to_warehouse=warehouse, qty=10)
+
+ so = make_sales_order(item_code=item, qty=10, rate=100)
+
+ pl = create_pick_list(so.name)
+ pl.save()
+ pl.reload()
+ self.assertEqual(pl.status, "Draft")
+
+ pl.submit()
+ pl.reload()
+ self.assertEqual(pl.status, "Open")
+
+ dn = create_delivery_note(pl.name)
+ dn.save()
+ pl.reload()
+ self.assertEqual(pl.status, "Open")
+
+ dn.submit()
+ pl.reload()
+ self.assertEqual(pl.status, "Completed")
+
+ dn.cancel()
+ pl.reload()
+ self.assertEqual(pl.status, "Completed")
+
+ pl.cancel()
+ pl.reload()
+ self.assertEqual(pl.status, "Cancelled")
+
+ def test_consider_existing_pick_list(self):
+ def create_items(items_properties):
+ items = []
+
+ for properties in items_properties:
+ properties.update({"maintain_stock": 1})
+ item_code = make_item(properties=properties).name
+ properties.update({"item_code": item_code})
+ items.append(properties)
+
+ return items
+
+ def create_stock_entries(items):
+ warehouses = ["Stores - _TC", "Finished Goods - _TC"]
+
+ for item in items:
+ for warehouse in warehouses:
+ se = make_stock_entry(
+ item=item.get("item_code"),
+ to_warehouse=warehouse,
+ qty=5,
+ )
+
+ def get_item_list(items, qty, warehouse="All Warehouses - _TC"):
+ return [
+ {
+ "item_code": item.get("item_code"),
+ "qty": qty,
+ "warehouse": warehouse,
+ }
+ for item in items
+ ]
+
+ def get_picked_items_details(pick_list_doc):
+ items_data = {}
+
+ for location in pick_list_doc.locations:
+ key = (location.warehouse, location.batch_no) if location.batch_no else location.warehouse
+ serial_no = [x for x in location.serial_no.split("\n") if x] if location.serial_no else None
+ data = {"picked_qty": location.picked_qty}
+ if serial_no:
+ data["serial_no"] = serial_no
+ if location.item_code not in items_data:
+ items_data[location.item_code] = {key: data}
+ else:
+ items_data[location.item_code][key] = data
+
+ return items_data
+
+ # Step - 1: Setup - Create Items and Stock Entries
+ items_properties = [
+ {
+ "valuation_rate": 100,
+ },
+ {
+ "valuation_rate": 200,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ },
+ {
+ "valuation_rate": 300,
+ "has_serial_no": 1,
+ "serial_no_series": "SNO.###",
+ },
+ {
+ "valuation_rate": 400,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "has_serial_no": 1,
+ "serial_no_series": "SNO.###",
+ },
+ ]
+
+ items = create_items(items_properties)
+ create_stock_entries(items)
+
+ # Step - 2: Create Sales Order [1]
+ so1 = make_sales_order(item_list=get_item_list(items, qty=6))
+
+ # Step - 3: Create and Submit Pick List [1] for Sales Order [1]
+ pl1 = create_pick_list(so1.name)
+ pl1.submit()
+
+ # Step - 4: Create Sales Order [2] with same Item(s) as Sales Order [1]
+ so2 = make_sales_order(item_list=get_item_list(items, qty=4))
+
+ # Step - 5: Create Pick List [2] for Sales Order [2]
+ pl2 = create_pick_list(so2.name)
+ pl2.save()
+
+ # Step - 6: Assert
+ picked_items_details = get_picked_items_details(pl1)
+
+ for location in pl2.locations:
+ key = (location.warehouse, location.batch_no) if location.batch_no else location.warehouse
+ item_data = picked_items_details.get(location.item_code, {}).get(key, {})
+ picked_qty = item_data.get("picked_qty", 0)
+ picked_serial_no = picked_items_details.get("serial_no", [])
+ bin_actual_qty = frappe.db.get_value(
+ "Bin", {"item_code": location.item_code, "warehouse": location.warehouse}, "actual_qty"
+ )
+
+ # Available Qty to pick should be equal to [Actual Qty - Picked Qty]
+ self.assertEqual(location.stock_qty, bin_actual_qty - picked_qty)
+
+ # Serial No should not be in the Picked Serial No list
+ if location.serial_no:
+ a = set(picked_serial_no)
+ b = set([x for x in location.serial_no.split("\n") if x])
+ self.assertSetEqual(b, b.difference(a))
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 1755f28..8c20ca0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -158,6 +158,7 @@
self.validate_subcontract_order()
self.update_subcontract_order_supplied_items()
self.update_subcontracting_order_status()
+ self.update_pick_list_status()
self.make_gl_entries()
@@ -2276,6 +2277,11 @@
update_subcontracting_order_status(self.subcontracting_order)
+ def update_pick_list_status(self):
+ from erpnext.stock.doctype.pick_list.pick_list import update_pick_list_status
+
+ update_pick_list_status(self.pick_list)
+
def set_missing_values(self):
"Updates rate and availability of all the items of mapped doc."
self.set_transfer_qty()