diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 96ad60f866..76123b1a0b 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '10.0.18'
+__version__ = '10.0.19'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 2cb8e1024a..ed3de07140 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -254,6 +254,36 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "allow_user_to_edit_discount",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Allow user to edit Discount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1476,7 +1506,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-01-03 17:30:45.198147",
+ "modified": "2018-01-31 19:33:11.765731",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f37fe6114c..2af30e1956 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -249,7 +249,8 @@ class SalesInvoice(SellingController):
if pos:
return {
"print_format": pos.get("print_format_for_online"),
- "allow_edit_rate": pos.get("allow_user_to_edit_rate")
+ "allow_edit_rate": pos.get("allow_user_to_edit_rate"),
+ "allow_edit_discount": pos.get("allow_user_to_edit_discount")
}
def update_time_sheet(self, sales_invoice):
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index ab5251f292..8917c9ff63 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -168,11 +168,11 @@ def get_tax_accounts(item_list, columns, company_currency,
for d in item_list:
invoice_item_row.setdefault(d.parent, []).append(d)
- item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
+ item_row_map.setdefault(d.parent, {}).setdefault(d.item_code or d.item_name, []).append(d)
conditions = ""
if doctype == "Purchase Invoice":
- conditions = " and category in ('Total', 'Valuation and Total')"
+ conditions = " and category in ('Total', 'Valuation and Total') and base_tax_amount_after_discount_amount != 0"
tax_details = frappe.db.sql("""
select
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 37848d5064..610475acfc 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -172,6 +172,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount
from `tabPurchase Taxes and Charges`
where parent in (%s) and category in ('Total', 'Valuation and Total')
+ and base_tax_amount_after_discount_amount != 0
group by parent, account_head, add_deduct_tax
""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index db13bd5520..e879f40c37 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -68,12 +68,16 @@ class PurchaseOrder(BuyingController):
},
"Supplier Quotation Item": {
"ref_dn_field": "supplier_quotation_item",
- "compare_fields": [["rate", "="], ["project", "="], ["item_code", "="],
+ "compare_fields": [["project", "="], ["item_code", "="],
["uom", "="], ["conversion_factor", "="]],
"is_child_table": True
}
})
+
+ if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
+ self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
+
def validate_supplier(self):
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
if prevent_po:
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index c395d0cfd7..b221a08959 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -124,7 +124,8 @@ def make_purchase_order(source_name, target_doc=None):
["name", "supplier_quotation_item"],
["parent", "supplier_quotation"],
["material_request", "material_request"],
- ["material_request_item", "material_request_item"]
+ ["material_request_item", "material_request_item"],
+ ["sales_order", "sales_order"]
],
"postprocess": update_item
},
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
index b3a55b645d..da78c12ba0 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -1326,6 +1326,37 @@
"unique": 0,
"width": "120px"
},
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sales_order",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Sales Order",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Sales Order",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -1614,7 +1645,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2017-12-14 09:37:47.427897",
+ "modified": "2018-01-25 15:04:40.171617",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",
diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py
index b97e097f08..ac11c7e45c 100644
--- a/erpnext/config/projects.py
+++ b/erpnext/config/projects.py
@@ -15,7 +15,7 @@ def get_data():
{
"type": "doctype",
"name": "Task",
- "route": "Tree/Task",
+ "route": "List/Task",
"description": _("Project activity / task."),
},
{
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index fb52d5260c..2e69475c9a 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -544,7 +544,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
group by item_code, stock_uom
order by idx"""
- if fetch_exploded:
+ if cint(fetch_exploded):
query = query.format(table="BOM Explosion Item",
where_conditions="",
select_columns = ", bom_item.source_warehouse, (Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx")
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index dbd098d119..9474a946e8 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -486,4 +486,5 @@ erpnext.patches.v10_0.add_guardian_role_for_parent_portal
erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank
erpnext.patches.v10_0.update_assessment_plan
erpnext.patches.v10_0.update_assessment_result
-erpnext.patches.v10_0.set_default_payment_terms_based_on_company
\ No newline at end of file
+erpnext.patches.v10_0.set_default_payment_terms_based_on_company
+erpnext.patches.v10_0.update_sales_order_link_to_purchase_order
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py
new file mode 100644
index 0000000000..b4f58384bf
--- /dev/null
+++ b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py
@@ -0,0 +1,18 @@
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("buying", "doctype", "supplier_quotation_item")
+
+ for doctype in ['Purchase Order','Supplier Quotation']:
+ frappe.db.sql("""
+ Update
+ `tab{doctype} Item`, `tabMaterial Request Item`
+ set
+ `tab{doctype} Item`.sales_order = `tabMaterial Request Item`.sales_order
+ where
+ `tab{doctype} Item`.material_request= `tabMaterial Request Item`.parent
+ and `tab{doctype} Item`.material_request_item = `tabMaterial Request Item`.name
+ and `tabMaterial Request Item`.sales_order is not null""".format(doctype=doctype))
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index f5bc6c2499..c20c6f8971 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -464,6 +464,7 @@ erpnext.pos.PointOfSale = class PointOfSale {
if (r.message) {
this.frm.meta.default_print_format = r.message.print_format || 'POS Invoice';
this.frm.allow_edit_rate = r.message.allow_edit_rate;
+ this.frm.allow_edit_discount = r.message.allow_edit_discount;
}
}
@@ -553,9 +554,9 @@ class POSCart {
${this.get_taxes_and_totals()}
-
- ${this.get_discount_amount()}
-
+ `+
+ (!this.frm.allow_edit_discount ? `` : `${this.get_discount_amount()}`)+
+ `
${this.get_grand_total()}
@@ -584,6 +585,7 @@ class POSCart {
this.numpad && this.numpad.reset_value();
this.customer_field.set_value("");
+ this.$discount_amount.find('input:text').val('');
this.wrapper.find('.grand-total-value').text(
format_currency(this.frm.doc.grand_total, this.frm.currency));
this.wrapper.find('.rounded-total-value').text(
@@ -714,6 +716,17 @@ class POSCart {
this.customer_field.set_value(this.frm.doc.customer);
}
+ disable_numpad_control() {
+ let disabled_btns = [];
+ if(!this.frm.allow_edit_rate) {
+ disabled_btns.push('Rate');
+ }
+ if(!this.frm.allow_edit_discount) {
+ disabled_btns.push('Disc');
+ }
+ return disabled_btns;
+ }
+
make_numpad() {
this.numpad = new NumberPad({
button_array: [
@@ -728,7 +741,7 @@ class POSCart {
disable_highlight: ['Qty', 'Disc', 'Rate', 'Pay'],
reset_btns: ['Qty', 'Disc', 'Rate', 'Pay'],
del_btn: 'Del',
- disable_btns: !this.frm.allow_edit_rate ? ['Rate']: [],
+ disable_btns: this.disable_numpad_control(),
wrapper: this.wrapper.find('.number-pad-container'),
onclick: (btn_value) => {
// on click
@@ -1268,7 +1281,7 @@ class NumberPad {
this.disable_highlight = disable_highlight;
this.reset_btns = reset_btns;
this.del_btn = del_btn;
- this.disable_btns = disable_btns;
+ this.disable_btns = disable_btns || [];
this.make_dom();
this.bind_events();
this.value = '';
@@ -1300,13 +1313,15 @@ class NumberPad {
this.set_class();
- this.disable_btns.forEach((btn) => {
- const $btn = this.get_btn(btn);
- $btn.prop("disabled", true)
- $btn.hover(() => {
- $btn.css('cursor','not-allowed');
+ if(this.disable_btns) {
+ this.disable_btns.forEach((btn) => {
+ const $btn = this.get_btn(btn);
+ $btn.prop("disabled", true)
+ $btn.hover(() => {
+ $btn.css('cursor','not-allowed');
+ })
})
- })
+ }
}
set_class() {
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 39172d7d11..14a478d7e4 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -9,6 +9,7 @@ from frappe.utils.nestedset import NestedSet
from frappe.website.website_generator import WebsiteGenerator
from frappe.website.render import clear_cache
from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
+from erpnext.utilities.product import get_qty_in_stock
class ItemGroup(NestedSet, WebsiteGenerator):
@@ -83,7 +84,8 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
# base query
query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group,
I.description, I.web_long_description as website_description, I.is_stock_item,
- case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock
+ case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock, I.website_warehouse,
+ I.has_batch_no
from `tabItem` I
left join tabBin S on I.item_code = S.item_code and I.website_warehouse = S.warehouse
where I.show_in_website = 1
@@ -104,8 +106,26 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1)
+ data = adjust_qty_for_expired_items(data)
+
return [get_item_for_list_in_html(r) for r in data]
+
+def adjust_qty_for_expired_items(data):
+ adjusted_data = []
+
+ for item in data:
+ if item.get('has_batch_no') and item.get('website_warehouse'):
+ stock_qty_dict = get_qty_in_stock(
+ item.get('name'), 'website_warehouse', item.get('website_warehouse'))
+ qty = stock_qty_dict.stock_qty[0][0]
+ item['in_stock'] = 1 if qty else 0
+ adjusted_data.append(item)
+
+ return adjusted_data
+
+
+
def get_child_groups(item_group_name):
item_group = frappe.get_doc("Item Group", item_group_name)
return frappe.db.sql("""select name
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index eca0475974..41bc7b4a11 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -47,7 +47,6 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None):
:param batch_no: Optional - give qty for this batch no
:param warehouse: Optional - give qty for this warehouse
:param item_code: Optional - give qty for this item'''
- frappe.has_permission('Batch', throw=True)
out = 0
if batch_no and warehouse:
out = float(frappe.db.sql("""select sum(actual_qty)
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index da310aa3d7..defce62e2e 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -65,7 +65,7 @@ class MaterialRequest(BuyingController):
self.status = "Draft"
from erpnext.controllers.status_updater import validate_status
- validate_status(self.status,
+ validate_status(self.status,
["Draft", "Submitted", "Stopped", "Cancelled", "Pending",
"Partially Ordered", "Ordered", "Issued", "Transferred"])
@@ -240,7 +240,8 @@ def make_purchase_order(source_name, target_doc=None):
["name", "material_request_item"],
["parent", "material_request"],
["uom", "stock_uom"],
- ["uom", "uom"]
+ ["uom", "uom"],
+ ["sales_order", "sales_order"]
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.stock_qty
@@ -344,7 +345,8 @@ def make_supplier_quotation(source_name, target_doc=None):
"doctype": "Supplier Quotation Item",
"field_map": {
"name": "material_request_item",
- "parent": "material_request"
+ "parent": "material_request",
+ "sales_order": "sales_order"
}
}
}, target_doc, postprocess)
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index a7638b4169..6b6723331a 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -472,8 +472,8 @@ def get_projected_qty(item_code, warehouse):
@frappe.whitelist()
def get_bin_details(item_code, warehouse):
return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
- ["projected_qty", "actual_qty"], as_dict=True) \
- or {"projected_qty": 0, "actual_qty": 0}
+ ["projected_qty", "actual_qty", "ordered_qty"], as_dict=True) \
+ or {"projected_qty": 0, "actual_qty": 0, "ordered_qty": 0}
@frappe.whitelist()
def get_serial_no_details(item_code, warehouse, stock_qty, serial_no):
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 8377f59979..e436132ec8 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -106,11 +106,10 @@ def get_opening_balance(filters, columns):
from erpnext.stock.stock_ledger import get_previous_sle
last_entry = get_previous_sle({
"item_code": filters.item_code,
- "warehouse": get_warehouse_condition(filters.warehouse),
+ "warehouse_condition": get_warehouse_condition(filters.warehouse),
"posting_date": filters.from_date,
"posting_time": "00:00:00"
})
-
row = [""]*len(columns)
row[1] = _("'Opening'")
for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')):
@@ -122,7 +121,7 @@ def get_warehouse_condition(warehouse):
warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1)
if warehouse_details:
return " exists (select name from `tabWarehouse` wh \
- where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft,
+ where wh.lft >= %s and wh.rgt <= %s and warehouse = wh.name)"%(warehouse_details.lft,
warehouse_details.rgt)
return ''
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 647c9faf02..874a382192 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -407,7 +407,12 @@ def get_previous_sle(args, for_update=False):
def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=None, for_update=False, debug=False):
"""get stock ledger entries filtered by specific posting datetime conditions"""
- conditions = "timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator)
+ conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator)
+ if previous_sle.get("warehouse"):
+ conditions += " and warehouse = %(warehouse)s"
+ elif previous_sle.get("warehouse_condition"):
+ conditions += " and " + previous_sle.get("warehouse_condition")
+
if not previous_sle.get("posting_date"):
previous_sle["posting_date"] = "1900-01-01"
if not previous_sle.get("posting_time"):
@@ -418,9 +423,8 @@ def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=No
return frappe.db.sql("""select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry`
where item_code = %%(item_code)s
- and warehouse = %%(warehouse)s
and ifnull(is_cancelled, 'No')='No'
- and %(conditions)s
+ %(conditions)s
order by timestamp(posting_date, posting_time) %(order)s, name %(order)s
%(limit)s %(for_update)s""" % {
"conditions": conditions,
diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py
index 9d5c056ba7..415056d80b 100644
--- a/erpnext/utilities/product.py
+++ b/erpnext/utilities/product.py
@@ -4,25 +4,70 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cint, fmt_money, flt
+from frappe.utils import cint, fmt_money, flt, nowdate, getdate
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
+from erpnext.stock.doctype.batch.batch import get_batch_qty
-def get_qty_in_stock(item_code, item_warehouse_field):
+def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
in_stock, stock_qty = 0, ''
template_item_code, is_stock_item = frappe.db.get_value("Item", item_code, ["variant_of", "is_stock_item"])
- warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field)
+ if not warehouse:
+ warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field)
+
if not warehouse and template_item_code and template_item_code != item_code:
warehouse = frappe.db.get_value("Item", template_item_code, item_warehouse_field)
if warehouse:
stock_qty = frappe.db.sql("""select GREATEST(actual_qty - reserved_qty, 0) from tabBin where
item_code=%s and warehouse=%s""", (item_code, warehouse))
- if stock_qty:
+
+ if stock_qty:
+ stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
+
+ if stock_qty:
+ in_stock = stock_qty[0][0] > 0 and 1 or 0
+
+ if stock_qty:
in_stock = stock_qty[0][0] > 0 and 1 or 0
return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item})
+
+def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
+ batches = frappe.get_all('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name'])
+ expired_batches = get_expired_batches(batches)
+ stock_qty = [list(item) for item in stock_qty]
+
+ for batch in expired_batches:
+ if warehouse:
+ stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse))
+ else:
+ stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch)))
+
+ if not stock_qty[0][0]:
+ break
+
+ return stock_qty
+
+
+def get_expired_batches(batches):
+ """
+ :param batches: A list of dict in the form [{'expiry_date': datetime.date(20XX, 1, 1), 'name': 'batch_id'}, ...]
+ """
+ return [b.name for b in batches if b.expiry_date and b.expiry_date <= getdate(nowdate())]
+
+
+def qty_from_all_warehouses(batch_info):
+ """
+ :param batch_info: A list of dict in the form [{u'warehouse': u'Stores - I', u'qty': 0.8}, ...]
+ """
+ qty = 0
+ for batch in batch_info:
+ qty = qty + batch.qty
+
+ return qty
+
def get_price(item_code, price_list, customer_group, company, qty=1):
template_item_code = frappe.db.get_value("Item", item_code, "variant_of")