[minor] notifications and other fixes to stock
This commit is contained in:
parent
d75ef108fc
commit
e91388381c
@ -351,7 +351,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-02-23 07:42:05.639225",
|
"modified": "2015-02-25 01:01:11.550217",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Order",
|
"name": "Production Order",
|
||||||
|
@ -14,9 +14,9 @@ def get_notification_config():
|
|||||||
"Contact": {"status":"Open"},
|
"Contact": {"status":"Open"},
|
||||||
"Opportunity": {"docstatus":0},
|
"Opportunity": {"docstatus":0},
|
||||||
"Quotation": {"docstatus":0},
|
"Quotation": {"docstatus":0},
|
||||||
"Sales Order": {"docstatus":0},
|
"Sales Order": { "per_delivered": ("<", 100) },
|
||||||
"Journal Entry": {"docstatus":0},
|
"Journal Entry": {"docstatus":0},
|
||||||
"Sales Invoice": {"docstatus":0},
|
"Sales Invoice": { "outstanding_amount": (">", 0) },
|
||||||
"Purchase Invoice": {"docstatus":0},
|
"Purchase Invoice": {"docstatus":0},
|
||||||
"Leave Application": {"status":"Open"},
|
"Leave Application": {"status":"Open"},
|
||||||
"Expense Claim": {"approval_status":"Draft"},
|
"Expense Claim": {"approval_status":"Draft"},
|
||||||
@ -25,8 +25,8 @@ def get_notification_config():
|
|||||||
"Delivery Note": {"docstatus":0},
|
"Delivery Note": {"docstatus":0},
|
||||||
"Stock Entry": {"docstatus":0},
|
"Stock Entry": {"docstatus":0},
|
||||||
"Material Request": {"docstatus":0},
|
"Material Request": {"docstatus":0},
|
||||||
"Purchase Order": {"docstatus":0},
|
"Purchase Order": { "per_received": ("<", 100) },
|
||||||
"Production Order": {"docstatus":0},
|
"Production Order": { "status": "In Process" },
|
||||||
"BOM": {"docstatus":0},
|
"BOM": {"docstatus":0},
|
||||||
"Timesheet": {"docstatus":0},
|
"Timesheet": {"docstatus":0},
|
||||||
"Time Log": {"status":"Draft"},
|
"Time Log": {"status":"Draft"},
|
||||||
|
@ -45,25 +45,25 @@ class StockEntry(StockController):
|
|||||||
self.get("__onload").credit_debit_note_exists = 1 if count else 0
|
self.get("__onload").credit_debit_note_exists = 1 if count else 0
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.pro_doc = None
|
||||||
|
if self.production_order:
|
||||||
|
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||||
|
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
self.validate_purpose()
|
self.validate_purpose()
|
||||||
pro_obj = self.production_order and \
|
|
||||||
frappe.get_doc('Production Order', self.production_order) or None
|
|
||||||
|
|
||||||
validate_fiscal_year(self.posting_date, self.fiscal_year, self.meta.get_label("posting_date"), self)
|
validate_fiscal_year(self.posting_date, self.fiscal_year, self.meta.get_label("posting_date"), self)
|
||||||
|
|
||||||
self.validate_item()
|
self.validate_item()
|
||||||
self.set_transfer_qty()
|
self.set_transfer_qty()
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
||||||
self.validate_warehouse(pro_obj)
|
self.validate_warehouse()
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
self.get_stock_and_rate()
|
self.get_stock_and_rate()
|
||||||
self.validate_bom()
|
self.validate_bom()
|
||||||
self.validate_finished_goods()
|
self.validate_finished_goods()
|
||||||
self.validate_return_reference_doc()
|
self.validate_return_reference_doc()
|
||||||
self.validate_with_material_request()
|
self.validate_with_material_request()
|
||||||
#self.validate_valuation_rate()
|
self.validate_valuation_rate()
|
||||||
self.set_total_incoming_outgoing_value()
|
self.set_total_incoming_outgoing_value()
|
||||||
self.set_total_amount()
|
self.set_total_amount()
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class StockEntry(StockController):
|
|||||||
frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
|
frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
|
||||||
frappe.MandatoryError)
|
frappe.MandatoryError)
|
||||||
|
|
||||||
def validate_warehouse(self, pro_obj):
|
def validate_warehouse(self):
|
||||||
"""perform various (sometimes conditional) validations on warehouse"""
|
"""perform various (sometimes conditional) validations on warehouse"""
|
||||||
|
|
||||||
source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract", "Material Transfer for Manufacture"]
|
source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract", "Material Transfer for Manufacture"]
|
||||||
@ -156,7 +156,7 @@ class StockEntry(StockController):
|
|||||||
if not d.t_warehouse:
|
if not d.t_warehouse:
|
||||||
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
|
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
|
||||||
|
|
||||||
elif pro_obj and cstr(d.t_warehouse) != pro_obj.fg_warehouse:
|
elif self.pro_doc and cstr(d.t_warehouse) != self.pro_doc.fg_warehouse:
|
||||||
frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx))
|
frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -272,37 +272,44 @@ class StockEntry(StockController):
|
|||||||
incoming_rate = flt(self.get_incoming_rate(args), self.precision("incoming_rate", d))
|
incoming_rate = flt(self.get_incoming_rate(args), self.precision("incoming_rate", d))
|
||||||
if incoming_rate > 0:
|
if incoming_rate > 0:
|
||||||
d.incoming_rate = incoming_rate
|
d.incoming_rate = incoming_rate
|
||||||
|
|
||||||
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
|
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
|
||||||
if not d.t_warehouse:
|
if not d.t_warehouse:
|
||||||
raw_material_cost += flt(d.amount)
|
raw_material_cost += flt(d.amount)
|
||||||
|
|
||||||
|
self.add_operation_cost(raw_material_cost, force)
|
||||||
|
|
||||||
|
def add_operation_cost(self, raw_material_cost, force):
|
||||||
|
"""Adds operating cost if Production Order is set"""
|
||||||
# set incoming rate for fg item
|
# set incoming rate for fg item
|
||||||
if self.purpose in ("Manufacture", "Repack"):
|
if self.production_order:
|
||||||
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||||
if not flt(d.incoming_rate) or force:
|
|
||||||
operation_cost_per_unit = self.get_operation_cost_per_unit(d.bom_no, d.qty)
|
operation_cost_per_unit = self.get_operation_cost_per_unit(d.bom_no, d.qty)
|
||||||
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
|
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
|
||||||
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d))
|
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d))
|
||||||
break
|
break
|
||||||
|
|
||||||
def get_operation_cost_per_unit(self, bom_no, qty):
|
def get_operation_cost_per_unit(self, bom_no, qty):
|
||||||
|
"""Returns operating cost from Production Order for given `bom_no`"""
|
||||||
operation_cost_per_unit = 0
|
operation_cost_per_unit = 0
|
||||||
|
|
||||||
if self.production_order:
|
if self.production_order:
|
||||||
pro_order = frappe.get_doc("Production Order", self.production_order)
|
if not getattr(self, "pro_doc", None):
|
||||||
for d in pro_order.get("operations"):
|
self.pro_doc = frappe.get_doc("Production Order", self.production_order)
|
||||||
|
for d in self.pro_doc.get("operations"):
|
||||||
if flt(d.completed_qty):
|
if flt(d.completed_qty):
|
||||||
operation_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
operation_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
||||||
else:
|
else:
|
||||||
operation_cost_per_unit += flt(d.planned_operating_cost) / flt(pro_order.qty)
|
operation_cost_per_unit += flt(d.planned_operating_cost) / flt(self.pro_doc.qty)
|
||||||
|
|
||||||
|
# set operating cost from BOM if specified.
|
||||||
if not operation_cost_per_unit and bom_no:
|
if not operation_cost_per_unit and bom_no:
|
||||||
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
||||||
operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
||||||
|
|
||||||
return operation_cost_per_unit + flt(self.additional_operating_cost) / flt(qty)
|
return operation_cost_per_unit + (flt(self.additional_operating_cost) / flt(qty))
|
||||||
|
|
||||||
def get_incoming_rate(self, args):
|
def get_incoming_rate(self, args):
|
||||||
incoming_rate = 0
|
incoming_rate = 0
|
||||||
@ -503,12 +510,12 @@ class StockEntry(StockController):
|
|||||||
self.set('items', [])
|
self.set('items', [])
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
|
|
||||||
pro_obj = None
|
|
||||||
if self.production_order:
|
if self.production_order:
|
||||||
# common validations
|
# common validations
|
||||||
pro_obj = frappe.get_doc('Production Order', self.production_order)
|
if not getattr(self, "pro_doc", None):
|
||||||
if pro_obj:
|
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||||
self.bom_no = pro_obj.bom_no
|
if self.pro_doc:
|
||||||
|
self.bom_no = self.pro_doc.bom_no
|
||||||
else:
|
else:
|
||||||
# invalid production order
|
# invalid production order
|
||||||
self.production_order = None
|
self.production_order = None
|
||||||
@ -517,18 +524,18 @@ class StockEntry(StockController):
|
|||||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
||||||
"Subcontract", "Material Transfer for Manufacture"]:
|
"Subcontract", "Material Transfer for Manufacture"]:
|
||||||
if self.production_order and self.purpose == "Material Transfer for Manufacture":
|
if self.production_order and self.purpose == "Material Transfer for Manufacture":
|
||||||
item_dict = self.get_pending_raw_materials(pro_obj)
|
item_dict = self.get_pending_raw_materials()
|
||||||
if self.to_warehouse and pro_obj:
|
if self.to_warehouse and self.pro_doc:
|
||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
item["to_warehouse"] = pro_obj.wip_warehouse
|
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
||||||
else:
|
else:
|
||||||
if not self.fg_completed_qty:
|
if not self.fg_completed_qty:
|
||||||
frappe.throw(_("Manufacturing Quantity is mandatory"))
|
frappe.throw(_("Manufacturing Quantity is mandatory"))
|
||||||
|
|
||||||
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
|
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
|
||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
if pro_obj:
|
if self.pro_doc:
|
||||||
item["from_warehouse"] = pro_obj.wip_warehouse
|
item["from_warehouse"] = self.pro_doc.wip_warehouse
|
||||||
|
|
||||||
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
|
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
|
||||||
|
|
||||||
@ -537,9 +544,14 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
# add finished goods item
|
# add finished goods item
|
||||||
if self.purpose in ("Manufacture", "Repack"):
|
if self.purpose in ("Manufacture", "Repack"):
|
||||||
|
self.load_items_from_bom()
|
||||||
|
|
||||||
|
self.get_stock_and_rate()
|
||||||
|
|
||||||
|
def load_items_from_bom(self):
|
||||||
if self.production_order:
|
if self.production_order:
|
||||||
item_code = pro_obj.production_item
|
item_code = self.pro_doc.production_item
|
||||||
to_warehouse = pro_obj.fg_warehouse
|
to_warehouse = self.pro_doc.fg_warehouse
|
||||||
else:
|
else:
|
||||||
item_code = frappe.db.get_value("BOM", self.bom_no, "item")
|
item_code = frappe.db.get_value("BOM", self.bom_no, "item")
|
||||||
to_warehouse = ""
|
to_warehouse = ""
|
||||||
@ -560,8 +572,6 @@ class StockEntry(StockController):
|
|||||||
}
|
}
|
||||||
}, bom_no = self.bom_no)
|
}, bom_no = self.bom_no)
|
||||||
|
|
||||||
self.get_stock_and_rate()
|
|
||||||
|
|
||||||
def get_bom_raw_materials(self, qty):
|
def get_bom_raw_materials(self, qty):
|
||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||||
|
|
||||||
@ -573,7 +583,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
return item_dict
|
return item_dict
|
||||||
|
|
||||||
def get_pending_raw_materials(self, pro_obj):
|
def get_pending_raw_materials(self):
|
||||||
"""
|
"""
|
||||||
issue (item quantity) that is pending to issue or desire to transfer,
|
issue (item quantity) that is pending to issue or desire to transfer,
|
||||||
whichever is less
|
whichever is less
|
||||||
@ -581,7 +591,7 @@ class StockEntry(StockController):
|
|||||||
item_dict = self.get_bom_raw_materials(1)
|
item_dict = self.get_bom_raw_materials(1)
|
||||||
issued_item_qty = self.get_issued_qty()
|
issued_item_qty = self.get_issued_qty()
|
||||||
|
|
||||||
max_qty = flt(pro_obj.qty)
|
max_qty = flt(self.pro_doc.qty)
|
||||||
only_pending_fetched = []
|
only_pending_fetched = []
|
||||||
|
|
||||||
for item in item_dict:
|
for item in item_dict:
|
||||||
|
@ -16,6 +16,7 @@ class StockReconciliation(StockController):
|
|||||||
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_posting_time()
|
||||||
self.remove_items_with_no_change()
|
self.remove_items_with_no_change()
|
||||||
self.validate_data()
|
self.validate_data()
|
||||||
self.validate_expense_account()
|
self.validate_expense_account()
|
||||||
|
@ -15,12 +15,10 @@
|
|||||||
<div class="col-sm-5 col-xs-4"><strong>{%= doc.item_code %}</strong>
|
<div class="col-sm-5 col-xs-4"><strong>{%= doc.item_code %}</strong>
|
||||||
{% if(doc.item_name != doc.item_code) { %}
|
{% if(doc.item_name != doc.item_code) { %}
|
||||||
<br>{%= doc.item_name %}{% } %}
|
<br>{%= doc.item_name %}{% } %}
|
||||||
<!-- {% if(doc.item_name != doc.description) { %}
|
|
||||||
<p>{%= doc.description %}</p>{% } %} -->
|
|
||||||
{% include "templates/form_grid/includes/visible_cols.html" %}
|
{% include "templates/form_grid/includes/visible_cols.html" %}
|
||||||
{% if(frm.doc.docstatus==0 && doc.s_warehouse && doc.actual_qty < doc.qty) { %}
|
{% if(frm.doc.docstatus==0 && doc.s_warehouse && doc.actual_qty < doc.qty) { %}
|
||||||
<span class="text-danger small" style="margin-left: 15px;">
|
<span class="text-danger small" style="margin-left: 15px;">
|
||||||
<span class="octicon octicon-stop" style="font-size: 12px;"></span> Not in Stock
|
Not in Stock
|
||||||
</span>
|
</span>
|
||||||
{% } %}
|
{% } %}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user