blob: e967f7061bb119e1704c771532799089a89eb5b9 [file] [log] [blame]
marination60261852021-04-13 00:39:26 +05301# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
Faris Ansarifd345f82017-10-05 11:17:30 +05302# License: GNU General Public License v3. See license.txt
3
Faris Ansarifd345f82017-10-05 11:17:30 +05304import frappe
Chillar Anand915b3432021-09-02 16:44:59 +05305from frappe.utils import cint, flt, fmt_money, getdate, nowdate
6
Faris Ansarifd345f82017-10-05 11:17:30 +05307from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
tundebabzyc14f1f12018-01-27 06:28:29 +01008from erpnext.stock.doctype.batch.batch import get_batch_qty
rohitwaghchauree6199dc2023-09-20 17:27:35 +05309from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
Faris Ansarifd345f82017-10-05 11:17:30 +053010
marination9fb61ef2022-02-01 00:39:14 +053011
marination60261852021-04-13 00:39:26 +053012def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
Ankush Menat494bd9e2022-03-28 18:52:46 +053013 in_stock, stock_qty = 0, ""
14 template_item_code, is_stock_item = frappe.db.get_value(
15 "Item", item_code, ["variant_of", "is_stock_item"]
16 )
Faris Ansarifd345f82017-10-05 11:17:30 +053017
tundebabzyc14f1f12018-01-27 06:28:29 +010018 if not warehouse:
marination60261852021-04-13 00:39:26 +053019 warehouse = frappe.db.get_value("Website Item", {"item_code": item_code}, item_warehouse_field)
tundebabzyc14f1f12018-01-27 06:28:29 +010020
Faris Ansarifd345f82017-10-05 11:17:30 +053021 if not warehouse and template_item_code and template_item_code != item_code:
Ankush Menat494bd9e2022-03-28 18:52:46 +053022 warehouse = frappe.db.get_value(
23 "Website Item", {"item_code": template_item_code}, item_warehouse_field
24 )
Faris Ansarifd345f82017-10-05 11:17:30 +053025
rohitwaghchauree6199dc2023-09-20 17:27:35 +053026 if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
27 warehouses = get_child_warehouses(warehouse)
28 else:
29 warehouses = [warehouse] if warehouse else []
Faris Ansarifd345f82017-10-05 11:17:30 +053030
rohitwaghchauree6199dc2023-09-20 17:27:35 +053031 total_stock = 0.0
32 if warehouses:
33 for warehouse in warehouses:
34 stock_qty = frappe.db.sql(
35 """
36 select GREATEST(S.actual_qty - S.reserved_qty - S.reserved_qty_for_production - S.reserved_qty_for_sub_contract, 0) / IFNULL(C.conversion_factor, 1)
37 from tabBin S
38 inner join `tabItem` I on S.item_code = I.Item_code
39 left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code
40 where S.item_code=%s and S.warehouse=%s""",
41 (item_code, warehouse),
42 )
43
44 if stock_qty:
45 total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
46
47 in_stock = total_stock > 0 and 1 or 0
tundebabzyc14f1f12018-01-27 06:28:29 +010048
Ankush Menat494bd9e2022-03-28 18:52:46 +053049 return frappe._dict(
rohitwaghchauree6199dc2023-09-20 17:27:35 +053050 {"in_stock": in_stock, "stock_qty": total_stock, "is_stock_item": is_stock_item}
Ankush Menat494bd9e2022-03-28 18:52:46 +053051 )
Faris Ansarifd345f82017-10-05 11:17:30 +053052
tundebabzyc14f1f12018-01-27 06:28:29 +010053
tundebabzy29c81422018-01-31 10:36:31 +010054def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
Ankush Menat494bd9e2022-03-28 18:52:46 +053055 batches = frappe.get_all("Batch", filters=[{"item": item_code}], fields=["expiry_date", "name"])
tundebabzyc14f1f12018-01-27 06:28:29 +010056 expired_batches = get_expired_batches(batches)
57 stock_qty = [list(item) for item in stock_qty]
Rushabh Mehta708e47a2018-08-08 16:37:31 +053058
tundebabzy29c81422018-01-31 10:36:31 +010059 for batch in expired_batches:
60 if warehouse:
61 stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse))
62 else:
63 stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch)))
tundebabzyc14f1f12018-01-27 06:28:29 +010064
tundebabzy29c81422018-01-31 10:36:31 +010065 if not stock_qty[0][0]:
66 break
67
rohitwaghchauree6199dc2023-09-20 17:27:35 +053068 return stock_qty[0][0] if stock_qty else 0
tundebabzyc14f1f12018-01-27 06:28:29 +010069
70
71def get_expired_batches(batches):
72 """
73 :param batches: A list of dict in the form [{'expiry_date': datetime.date(20XX, 1, 1), 'name': 'batch_id'}, ...]
74 """
75 return [b.name for b in batches if b.expiry_date and b.expiry_date <= getdate(nowdate())]
76
77
78def qty_from_all_warehouses(batch_info):
79 """
80 :param batch_info: A list of dict in the form [{u'warehouse': u'Stores - I', u'qty': 0.8}, ...]
81 """
82 qty = 0
83 for batch in batch_info:
84 qty = qty + batch.qty
85
86 return qty
87
Ankush Menat494bd9e2022-03-28 18:52:46 +053088
Faris Ansarifd345f82017-10-05 11:17:30 +053089def get_price(item_code, price_list, customer_group, company, qty=1):
Devin Slauenwhite282fbf42021-12-18 16:03:16 -050090 from erpnext.e_commerce.shopping_cart.cart import get_party
91
Faris Ansarifd345f82017-10-05 11:17:30 +053092 template_item_code = frappe.db.get_value("Item", item_code, "variant_of")
93
94 if price_list:
Ankush Menat494bd9e2022-03-28 18:52:46 +053095 price = frappe.get_all(
96 "Item Price",
97 fields=["price_list_rate", "currency"],
98 filters={"price_list": price_list, "item_code": item_code},
99 )
Faris Ansarifd345f82017-10-05 11:17:30 +0530100
101 if template_item_code and not price:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530102 price = frappe.get_all(
103 "Item Price",
104 fields=["price_list_rate", "currency"],
105 filters={"price_list": price_list, "item_code": template_item_code},
106 )
Faris Ansarifd345f82017-10-05 11:17:30 +0530107
108 if price:
Devin Slauenwhite282fbf42021-12-18 16:03:16 -0500109 party = get_party()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530110 pricing_rule_dict = frappe._dict(
111 {
112 "item_code": item_code,
113 "qty": qty,
114 "stock_qty": qty,
115 "transaction_type": "selling",
116 "price_list": price_list,
117 "customer_group": customer_group,
118 "company": company,
119 "conversion_rate": 1,
120 "for_shopping_cart": True,
121 "currency": frappe.db.get_value("Price List", price_list, "currency"),
Deepesh Garg6192af52022-12-08 18:04:40 +0530122 "doctype": "Quotation",
Ankush Menat494bd9e2022-03-28 18:52:46 +0530123 }
124 )
Devin Slauenwhite282fbf42021-12-18 16:03:16 -0500125
126 if party and party.doctype == "Customer":
127 pricing_rule_dict.update({"customer": party.name})
128
129 pricing_rule = get_pricing_rule_for_item(pricing_rule_dict)
marination60261852021-04-13 00:39:26 +0530130 price_obj = price[0]
Faris Ansarifd345f82017-10-05 11:17:30 +0530131
132 if pricing_rule:
marination60261852021-04-13 00:39:26 +0530133 # price without any rules applied
134 mrp = price_obj.price_list_rate or 0
135
Faris Ansarifd345f82017-10-05 11:17:30 +0530136 if pricing_rule.pricing_rule_for == "Discount Percentage":
marination1d949142021-04-20 21:54:52 +0530137 price_obj.discount_percent = pricing_rule.discount_percentage
marination60261852021-04-13 00:39:26 +0530138 price_obj.formatted_discount_percent = str(flt(pricing_rule.discount_percentage, 0)) + "%"
Ankush Menat494bd9e2022-03-28 18:52:46 +0530139 price_obj.price_list_rate = flt(
140 price_obj.price_list_rate * (1.0 - (flt(pricing_rule.discount_percentage) / 100.0))
141 )
Faris Ansarifd345f82017-10-05 11:17:30 +0530142
Charles-Henri Decultot614959d2018-08-06 11:12:04 +0200143 if pricing_rule.pricing_rule_for == "Rate":
marination60261852021-04-13 00:39:26 +0530144 rate_discount = flt(mrp) - flt(pricing_rule.price_list_rate)
145 if rate_discount > 0:
146 price_obj.formatted_discount_rate = fmt_money(rate_discount, currency=price_obj["currency"])
147 price_obj.price_list_rate = pricing_rule.price_list_rate or 0
Faris Ansarifd345f82017-10-05 11:17:30 +0530148
Faris Ansarifd345f82017-10-05 11:17:30 +0530149 if price_obj:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530150 price_obj["formatted_price"] = fmt_money(
151 price_obj["price_list_rate"], currency=price_obj["currency"]
152 )
marination60261852021-04-13 00:39:26 +0530153 if mrp != price_obj["price_list_rate"]:
154 price_obj["formatted_mrp"] = fmt_money(mrp, currency=price_obj["currency"])
Faris Ansarifd345f82017-10-05 11:17:30 +0530155
Ankush Menat494bd9e2022-03-28 18:52:46 +0530156 price_obj["currency_symbol"] = (
157 not cint(frappe.db.get_default("hide_currency_symbol"))
158 and (
159 frappe.db.get_value("Currency", price_obj.currency, "symbol", cache=True)
160 or price_obj.currency
161 )
Faris Ansarifd345f82017-10-05 11:17:30 +0530162 or ""
Ankush Menat494bd9e2022-03-28 18:52:46 +0530163 )
Faris Ansarifd345f82017-10-05 11:17:30 +0530164
Ankush Menat494bd9e2022-03-28 18:52:46 +0530165 uom_conversion_factor = frappe.db.sql(
166 """select C.conversion_factor
Britloge3a9b9f2018-03-20 09:58:44 +0100167 from `tabUOM Conversion Detail` C
Britlog7d9689c2018-11-13 07:06:41 +0100168 inner join `tabItem` I on C.parent = I.name and C.uom = I.sales_uom
Ankush Menat494bd9e2022-03-28 18:52:46 +0530169 where I.name = %s""",
170 item_code,
171 )
Britloge3a9b9f2018-03-20 09:58:44 +0100172
173 uom_conversion_factor = uom_conversion_factor[0][0] if uom_conversion_factor else 1
Ankush Menat494bd9e2022-03-28 18:52:46 +0530174 price_obj["formatted_price_sales_uom"] = fmt_money(
175 price_obj["price_list_rate"] * uom_conversion_factor, currency=price_obj["currency"]
176 )
Britloge3a9b9f2018-03-20 09:58:44 +0100177
Faris Ansarifd345f82017-10-05 11:17:30 +0530178 if not price_obj["price_list_rate"]:
179 price_obj["price_list_rate"] = 0
180
181 if not price_obj["currency"]:
182 price_obj["currency"] = ""
183
184 if not price_obj["formatted_price"]:
marination60261852021-04-13 00:39:26 +0530185 price_obj["formatted_price"], price_obj["formatted_mrp"] = "", ""
Faris Ansarifd345f82017-10-05 11:17:30 +0530186
187 return price_obj
Marica40dffab2020-01-24 15:52:27 +0530188
Ankush Menat494bd9e2022-03-28 18:52:46 +0530189
Marica40dffab2020-01-24 15:52:27 +0530190def get_non_stock_item_status(item_code, item_warehouse_field):
marination60261852021-04-13 00:39:26 +0530191 # if item is a product bundle, check if its bundle items are in stock
Marica40dffab2020-01-24 15:52:27 +0530192 if frappe.db.exists("Product Bundle", item_code):
193 items = frappe.get_doc("Product Bundle", item_code).get_all_children()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530194 bundle_warehouse = frappe.db.get_value(
195 "Website Item", {"item_code": item_code}, item_warehouse_field
196 )
197 return all(
198 get_web_item_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock
199 for d in items
200 )
Marica40dffab2020-01-24 15:52:27 +0530201 else:
202 return 1