feat: Apply Putaway Rules within transaction itself
- Added checkbox 'Apply Putaway Rule' in PR and SE - Added link to rule in child tables - Rule is applied on Save - Validation for over receipt - Apply Rule on Stock Entry as well for Material Transfer and Receipt
This commit is contained in:
@@ -349,9 +349,7 @@ def close_or_unclose_purchase_orders(names, status):
|
|||||||
frappe.local.message_log = []
|
frappe.local.message_log = []
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
|
|
||||||
target.ignore_pricing_rule = 1
|
target.ignore_pricing_rule = 1
|
||||||
target.items = apply_putaway_rule(target.items, target.company)
|
|
||||||
target.run_method("set_missing_values")
|
target.run_method("set_missing_values")
|
||||||
target.run_method("calculate_taxes_and_totals")
|
target.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import frappe, erpnext
|
|||||||
from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
|
from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
from collections import defaultdict
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
@@ -23,6 +24,7 @@ class StockController(AccountsController):
|
|||||||
self.validate_inspection()
|
self.validate_inspection()
|
||||||
self.validate_serialized_batch()
|
self.validate_serialized_batch()
|
||||||
self.validate_customer_provided_item()
|
self.validate_customer_provided_item()
|
||||||
|
self.validate_putaway_capacity()
|
||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None):
|
def make_gl_entries(self, gl_entries=None):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@@ -399,6 +401,42 @@ class StockController(AccountsController):
|
|||||||
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
|
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
|
||||||
d.allow_zero_valuation_rate = 1
|
d.allow_zero_valuation_rate = 1
|
||||||
|
|
||||||
|
def validate_putaway_capacity(self):
|
||||||
|
# if over receipt is attempted while 'apply putaway rule' is disabled
|
||||||
|
# and if rule was applied on the transaction, validate it.
|
||||||
|
from erpnext.stock.doctype.putaway_rule.putaway_rule import get_putaway_capacity
|
||||||
|
valid_doctype = self.doctype in ("Purchase Receipt", "Stock Entry")
|
||||||
|
rule_applied = any(item.get("putaway_rule") for item in self.get("items"))
|
||||||
|
|
||||||
|
if valid_doctype and rule_applied and not self.apply_putaway_rule:
|
||||||
|
rule_map = defaultdict(dict)
|
||||||
|
for item in self.get("items"):
|
||||||
|
if item.get("putaway_rule"):
|
||||||
|
rule = item.get("putaway_rule")
|
||||||
|
disabled = frappe.db.get_value("Putaway Rule", rule, "disable")
|
||||||
|
if disabled: return # dont validate for disabled rule
|
||||||
|
stock_qty = flt(item.transfer_qty) if self.doctype == "Stock Entry" else flt(item.stock_qty)
|
||||||
|
warehouse_field = "t_warehouse" if self.doctype == "Stock Entry" else "warehouse"
|
||||||
|
if not rule_map[rule]:
|
||||||
|
rule_map[rule]["warehouse"] = item.get(warehouse_field)
|
||||||
|
rule_map[rule]["item"] = item.get("item_code")
|
||||||
|
rule_map[rule]["qty_put"] = 0
|
||||||
|
rule_map[rule]["capacity"] = get_putaway_capacity(rule)
|
||||||
|
rule_map[rule]["qty_put"] += flt(stock_qty)
|
||||||
|
|
||||||
|
for rule, values in rule_map.items():
|
||||||
|
if flt(values["qty_put"]) > flt(values["capacity"]):
|
||||||
|
message = _("{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.") \
|
||||||
|
.format(
|
||||||
|
frappe.bold(values["qty_put"]), frappe.bold(values["item"]),
|
||||||
|
frappe.bold(values["warehouse"]), frappe.bold(values["capacity"])
|
||||||
|
)
|
||||||
|
message += "<br><br>"
|
||||||
|
rule_link = frappe.utils.get_link_to_form("Putaway Rule", rule)
|
||||||
|
message += _(" Please adjust the qty or edit {0} to proceed.").format(rule_link)
|
||||||
|
frappe.throw(msg=message, title=_("Over Receipt"))
|
||||||
|
return rule_map
|
||||||
|
|
||||||
def compare_existing_and_expected_gle(existing_gle, expected_gle):
|
def compare_existing_and_expected_gle(existing_gle, expected_gle):
|
||||||
matched = True
|
matched = True
|
||||||
for entry in expected_gle:
|
for entry in expected_gle:
|
||||||
|
|||||||
@@ -524,3 +524,26 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
|
|||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.apply_putaway_rule = (frm) => {
|
||||||
|
if (!frm.doc.company) {
|
||||||
|
frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")})
|
||||||
|
}
|
||||||
|
if (!frm.doc.items.length) return;
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.stock.doctype.putaway_rule.putaway_rule.apply_putaway_rule",
|
||||||
|
args: {
|
||||||
|
items: frm.doc.items,
|
||||||
|
company: frm.doc.company
|
||||||
|
},
|
||||||
|
callback: (result) => {
|
||||||
|
if(!result.exc) {
|
||||||
|
if(result.message) {
|
||||||
|
frm.doc.items = result.message;
|
||||||
|
frm.get_field("items").refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -213,6 +213,10 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_putaway_rule: function() {
|
||||||
|
// if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// for backward compatibility: combine new and previous states
|
// for backward compatibility: combine new and previous states
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ class PurchaseReceipt(BuyingController):
|
|||||||
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def before_save(self):
|
||||||
|
from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
|
||||||
|
|
||||||
|
if self.get("items") and self.apply_putaway_rule:
|
||||||
|
self.items = apply_putaway_rule(self.doctype, self.get("items"), self.company)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
super(PurchaseReceipt, self).validate()
|
super(PurchaseReceipt, self).validate()
|
||||||
@@ -90,6 +96,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if getdate(self.posting_date) > getdate(nowdate()):
|
if getdate(self.posting_date) > getdate(nowdate()):
|
||||||
throw(_("Posting Date cannot be future date"))
|
throw(_("Posting Date cannot be future date"))
|
||||||
|
|
||||||
|
|
||||||
def validate_cwip_accounts(self):
|
def validate_cwip_accounts(self):
|
||||||
for item in self.get('items'):
|
for item in self.get('items'):
|
||||||
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"purchase_order_item",
|
"purchase_order_item",
|
||||||
"material_request_item",
|
"material_request_item",
|
||||||
"purchase_receipt_item",
|
"purchase_receipt_item",
|
||||||
|
"putaway_rule",
|
||||||
"section_break_45",
|
"section_break_45",
|
||||||
"allow_zero_valuation_rate",
|
"allow_zero_valuation_rate",
|
||||||
"bom",
|
"bom",
|
||||||
@@ -834,12 +835,21 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "image_column",
|
"fieldname": "image_column",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "putaway_rule",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Putaway Rule",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Putaway Rule",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-28 19:01:21.154963",
|
"modified": "2020-11-26 12:16:14.897160",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from six import string_types
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, floor, nowdate
|
from frappe.utils import flt, floor, nowdate, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.stock.utils import get_stock_balance
|
from erpnext.stock.utils import get_stock_balance
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
class PutawayRule(Document):
|
class PutawayRule(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -52,70 +55,50 @@ class PutawayRule(Document):
|
|||||||
def set_stock_capacity(self):
|
def set_stock_capacity(self):
|
||||||
self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity)
|
self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity)
|
||||||
|
|
||||||
def get_ordered_putaway_rules(item_code, company):
|
@frappe.whitelist()
|
||||||
"""Returns an ordered list of putaway rules to apply on an item."""
|
def get_putaway_capacity(rule):
|
||||||
rules = frappe.get_all("Putaway Rule",
|
stock_capacity, item_code, warehouse = frappe.db.get_value("Putaway Rule", rule,
|
||||||
fields=["name", "item_code", "stock_capacity", "priority", "warehouse"],
|
["stock_capacity", "item_code", "warehouse"])
|
||||||
filters={"item_code": item_code, "company": company, "disable": 0},
|
balance_qty = get_stock_balance(item_code, warehouse, nowdate())
|
||||||
order_by="priority asc, capacity desc")
|
free_space = flt(stock_capacity) - flt(balance_qty)
|
||||||
|
return free_space if free_space > 0 else 0
|
||||||
if not rules:
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
for rule in rules:
|
|
||||||
balance_qty = get_stock_balance(rule.item_code, rule.warehouse, nowdate())
|
|
||||||
free_space = flt(rule.stock_capacity) - flt(balance_qty)
|
|
||||||
if free_space > 0:
|
|
||||||
rule["free_space"] = free_space
|
|
||||||
else:
|
|
||||||
del rule
|
|
||||||
|
|
||||||
if not rules:
|
|
||||||
# After iterating through rules, if no rules are left
|
|
||||||
# then there is not enough space left in any rule
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
rules = sorted(rules, key = lambda i: (i['priority'], -i['free_space']))
|
|
||||||
return False, rules
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def apply_putaway_rule(items, company):
|
def apply_putaway_rule(doctype, items, company):
|
||||||
""" Applies Putaway Rule on line items.
|
""" Applies Putaway Rule on line items.
|
||||||
|
|
||||||
items: List of Purchase Receipt Item objects
|
items: List of Purchase Receipt Item objects
|
||||||
company: Company in the Purchase Receipt
|
company: Company in the Purchase Receipt
|
||||||
"""
|
"""
|
||||||
|
if isinstance(items, string_types):
|
||||||
|
items = json.loads(items)
|
||||||
|
|
||||||
items_not_accomodated, updated_table = [], []
|
items_not_accomodated, updated_table = [], []
|
||||||
item_wise_rules = defaultdict(list)
|
item_wise_rules = defaultdict(list)
|
||||||
|
|
||||||
def add_row(item, to_allocate, warehouse):
|
|
||||||
new_updated_table_row = copy.deepcopy(item)
|
|
||||||
new_updated_table_row.name = ''
|
|
||||||
new_updated_table_row.idx = 1 if not updated_table else flt(updated_table[-1].idx) + 1
|
|
||||||
new_updated_table_row.qty = to_allocate
|
|
||||||
new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
|
|
||||||
new_updated_table_row.warehouse = warehouse
|
|
||||||
updated_table.append(new_updated_table_row)
|
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
conversion = flt(item.conversion_factor)
|
if isinstance(item, dict):
|
||||||
uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
|
item = frappe._dict(item)
|
||||||
pending_qty, pending_stock_qty, item_code = flt(item.qty), flt(item.stock_qty), item.item_code
|
|
||||||
|
|
||||||
if not pending_qty:
|
source_warehouse = item.get("s_warehouse")
|
||||||
add_row(item, pending_qty, item.warehouse)
|
serial_nos = get_serial_nos(item.get("serial_no"))
|
||||||
|
conversion = flt(item.conversion_factor) or 1
|
||||||
|
pending_qty, item_code = flt(item.qty), item.item_code
|
||||||
|
pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
|
||||||
|
if not pending_qty or not item_code:
|
||||||
|
updated_table = add_row(item, pending_qty, item.warehouse, updated_table)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
at_capacity, rules = get_ordered_putaway_rules(item_code, company)
|
uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
|
||||||
|
|
||||||
|
at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
|
||||||
|
|
||||||
if not rules:
|
if not rules:
|
||||||
|
warehouse = item.warehouse
|
||||||
if at_capacity:
|
if at_capacity:
|
||||||
# rules available, but no free space
|
warehouse = '' # rules available, but no free space
|
||||||
add_row(item, pending_qty, '')
|
|
||||||
items_not_accomodated.append([item_code, pending_qty])
|
items_not_accomodated.append([item_code, pending_qty])
|
||||||
else:
|
updated_table = add_row(item, pending_qty, warehouse, updated_table)
|
||||||
# no rules to apply
|
|
||||||
add_row(item, pending_qty, item.warehouse)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# maintain item wise rules, to handle if item is entered twice
|
# maintain item wise rules, to handle if item is entered twice
|
||||||
@@ -126,7 +109,7 @@ def apply_putaway_rule(items, company):
|
|||||||
for rule in item_wise_rules[item_code]:
|
for rule in item_wise_rules[item_code]:
|
||||||
if pending_stock_qty > 0 and rule.free_space:
|
if pending_stock_qty > 0 and rule.free_space:
|
||||||
stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
|
stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
|
||||||
qty_to_allocate = stock_qty_to_allocate / (conversion or 1)
|
qty_to_allocate = stock_qty_to_allocate / (conversion)
|
||||||
|
|
||||||
if uom_must_be_whole_number:
|
if uom_must_be_whole_number:
|
||||||
qty_to_allocate = floor(qty_to_allocate)
|
qty_to_allocate = floor(qty_to_allocate)
|
||||||
@@ -134,7 +117,8 @@ def apply_putaway_rule(items, company):
|
|||||||
|
|
||||||
if not qty_to_allocate: break
|
if not qty_to_allocate: break
|
||||||
|
|
||||||
add_row(item, qty_to_allocate, rule.warehouse)
|
updated_table = add_row(item, qty_to_allocate, rule.warehouse, updated_table,
|
||||||
|
rule.name, serial_nos=serial_nos)
|
||||||
|
|
||||||
pending_stock_qty -= stock_qty_to_allocate
|
pending_stock_qty -= stock_qty_to_allocate
|
||||||
pending_qty -= qty_to_allocate
|
pending_qty -= qty_to_allocate
|
||||||
@@ -144,15 +128,71 @@ def apply_putaway_rule(items, company):
|
|||||||
|
|
||||||
# if pending qty after applying all rules, add row without warehouse
|
# if pending qty after applying all rules, add row without warehouse
|
||||||
if pending_stock_qty > 0:
|
if pending_stock_qty > 0:
|
||||||
add_row(item, pending_qty, '')
|
# updated_table = add_row(item, pending_qty, '', updated_table, serial_nos=serial_nos)
|
||||||
items_not_accomodated.append([item.item_code, pending_qty])
|
items_not_accomodated.append([item.item_code, pending_qty])
|
||||||
|
|
||||||
if items_not_accomodated:
|
if items_not_accomodated:
|
||||||
format_unassigned_items_error(items_not_accomodated)
|
show_unassigned_items_message(items_not_accomodated)
|
||||||
|
|
||||||
return updated_table if updated_table else items
|
return updated_table if updated_table else items
|
||||||
|
|
||||||
def format_unassigned_items_error(items_not_accomodated):
|
def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
|
||||||
|
"""Returns an ordered list of putaway rules to apply on an item."""
|
||||||
|
filters = {
|
||||||
|
"item_code": item_code,
|
||||||
|
"company": company,
|
||||||
|
"disable": 0
|
||||||
|
}
|
||||||
|
if source_warehouse:
|
||||||
|
filters.update({"warehouse": ["!=", source_warehouse]})
|
||||||
|
|
||||||
|
rules = frappe.get_all("Putaway Rule",
|
||||||
|
fields=["name", "item_code", "stock_capacity", "priority", "warehouse"],
|
||||||
|
filters=filters,
|
||||||
|
order_by="priority asc, capacity desc")
|
||||||
|
|
||||||
|
if not rules:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
vacant_rules = []
|
||||||
|
for rule in rules:
|
||||||
|
balance_qty = get_stock_balance(rule.item_code, rule.warehouse, nowdate())
|
||||||
|
free_space = flt(rule.stock_capacity) - flt(balance_qty)
|
||||||
|
if free_space > 0:
|
||||||
|
rule["free_space"] = free_space
|
||||||
|
vacant_rules.append(rule)
|
||||||
|
|
||||||
|
if not vacant_rules:
|
||||||
|
# After iterating through rules, if no rules are left
|
||||||
|
# then there is not enough space left in any rule
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
vacant_rules = sorted(vacant_rules, key = lambda i: (i['priority'], -i['free_space']))
|
||||||
|
|
||||||
|
return False, vacant_rules
|
||||||
|
|
||||||
|
def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=None):
|
||||||
|
new_updated_table_row = copy.deepcopy(item)
|
||||||
|
new_updated_table_row.idx = 1 if not updated_table else cint(updated_table[-1].idx) + 1
|
||||||
|
new_updated_table_row.name = "New " + str(item.doctype) + " " + str(new_updated_table_row.idx)
|
||||||
|
new_updated_table_row.qty = to_allocate
|
||||||
|
new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
|
||||||
|
if item.doctype == "Stock Entry Detail":
|
||||||
|
new_updated_table_row.t_warehouse = warehouse
|
||||||
|
else:
|
||||||
|
new_updated_table_row.warehouse = warehouse
|
||||||
|
new_updated_table_row.rejected_qty = 0
|
||||||
|
new_updated_table_row.received_qty = to_allocate
|
||||||
|
|
||||||
|
if rule:
|
||||||
|
new_updated_table_row.putaway_rule = rule
|
||||||
|
if serial_nos:
|
||||||
|
new_updated_table_row.serial_no = get_serial_nos_to_allocate(serial_nos, to_allocate)
|
||||||
|
|
||||||
|
updated_table.append(new_updated_table_row)
|
||||||
|
return updated_table
|
||||||
|
|
||||||
|
def show_unassigned_items_message(items_not_accomodated):
|
||||||
msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "<br><br>"
|
msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "<br><br>"
|
||||||
formatted_item_rows = ""
|
formatted_item_rows = ""
|
||||||
|
|
||||||
@@ -173,4 +213,11 @@ def format_unassigned_items_error(items_not_accomodated):
|
|||||||
</table>
|
</table>
|
||||||
""".format(_("Item"), _("Unassigned Qty"), formatted_item_rows)
|
""".format(_("Item"), _("Unassigned Qty"), formatted_item_rows)
|
||||||
|
|
||||||
frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
|
frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
|
||||||
|
|
||||||
|
def get_serial_nos_to_allocate(serial_nos, to_allocate):
|
||||||
|
if serial_nos:
|
||||||
|
allocated_serial_nos = serial_nos[0: cint(to_allocate)]
|
||||||
|
serial_nos[:] = serial_nos[cint(to_allocate):] # pop out allocated serial nos and modify list
|
||||||
|
return "\n".join(allocated_serial_nos) if allocated_serial_nos else ""
|
||||||
|
else: return ""
|
||||||
@@ -571,6 +571,10 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
apply_putaway_rule: function(frm) {
|
||||||
|
// if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"set_posting_time",
|
"set_posting_time",
|
||||||
"inspection_required",
|
"inspection_required",
|
||||||
"from_bom",
|
"from_bom",
|
||||||
|
"apply_putaway_rule",
|
||||||
"sb1",
|
"sb1",
|
||||||
"bom_no",
|
"bom_no",
|
||||||
"fg_completed_qty",
|
"fg_completed_qty",
|
||||||
@@ -640,13 +641,20 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Add to Transit",
|
"label": "Add to Transit",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], doc.purpose)",
|
||||||
|
"fieldname": "apply_putaway_rule",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Apply Putaway Rule"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-11 19:10:07.954981",
|
"modified": "2020-12-07 14:58:13.267321",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry",
|
"name": "Stock Entry",
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ class StockEntry(StockController):
|
|||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
item.update(get_bin_details(item.item_code, item.s_warehouse))
|
item.update(get_bin_details(item.item_code, item.s_warehouse))
|
||||||
|
|
||||||
|
def before_save(self):
|
||||||
|
from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
|
||||||
|
apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
|
||||||
|
|
||||||
|
if self.get("items") and apply_rule:
|
||||||
|
self.items = apply_putaway_rule(self.doctype, self.get("items"), self.company)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.pro_doc = frappe._dict()
|
self.pro_doc = frappe._dict()
|
||||||
if self.work_order:
|
if self.work_order:
|
||||||
@@ -79,6 +86,7 @@ class StockEntry(StockController):
|
|||||||
self.validate_serialized_batch()
|
self.validate_serialized_batch()
|
||||||
self.set_actual_qty()
|
self.set_actual_qty()
|
||||||
self.calculate_rate_and_amount(update_finished_item_rate=False)
|
self.calculate_rate_and_amount(update_finished_item_rate=False)
|
||||||
|
self.validate_putaway_capacity()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-03-29 18:22:12",
|
"creation": "2013-03-29 18:22:12",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"against_stock_entry",
|
"against_stock_entry",
|
||||||
"ste_detail",
|
"ste_detail",
|
||||||
"po_detail",
|
"po_detail",
|
||||||
|
"putaway_rule",
|
||||||
"column_break_51",
|
"column_break_51",
|
||||||
"transferred_qty",
|
"transferred_qty",
|
||||||
"reference_purchase_receipt",
|
"reference_purchase_receipt",
|
||||||
@@ -498,13 +500,23 @@
|
|||||||
"fieldname": "set_basic_rate_manually",
|
"fieldname": "set_basic_rate_manually",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Set Basic Rate Manually"
|
"label": "Set Basic Rate Manually"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], parent.purpose)",
|
||||||
|
"fieldname": "putaway_rule",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Putaway Rule",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Putaway Rule",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-09-23 17:55:03.384138",
|
"modified": "2020-12-07 15:00:44.489442",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry Detail",
|
"name": "Stock Entry Detail",
|
||||||
|
|||||||
Reference in New Issue
Block a user