feat: added good in transit feature in the stock entry

This commit is contained in:
Rohit Waghchaure 2019-03-14 16:19:08 +05:30
parent 1f2f9cf360
commit 186c758087
7 changed files with 4200 additions and 3748 deletions

View File

@ -19,7 +19,8 @@ def create_stock_entry_types():
for purpose in ["Material Issue", "Material Receipt", "Material Transfer",
"Material Transfer for Manufacture", "Material Consumption for Manufacture", "Manufacture",
"Repack", "Subcontract"]:
"Repack", "Subcontract", "Stock Out", "Stock In"]:
ste_type = frappe.get_doc({
'doctype': 'Stock Entry Type',
'name': purpose,
@ -31,6 +32,8 @@ def create_stock_entry_types():
except frappe.DuplicateEntryError:
pass
frappe.db.sql(" UPDATE `tabStock Entry` set stock_entry_type = purpose ")
def add_gst_hsn_code_field():
custom_fields = {
'Stock Entry Detail': [dict(fieldname='gst_hsn_code', label='HSN/SAC',

View File

@ -14,6 +14,16 @@ frappe.ui.form.on('Stock Entry', {
}
});
frm.set_query('outward_stock_entry', function() {
return {
filters: [
['Stock Entry', 'docstatus', '=', 1],
['Stock Entry', 'per_transferred', '<','100'],
['Stock Entry', 'purpose', '=', 'Stock Out']
]
}
});
frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => {
if (r.sample_retention_warehouse) {
var filters = [
@ -92,6 +102,16 @@ frappe.ui.form.on('Stock Entry', {
});
},
outward_stock_entry: function(frm) {
frappe.call({
doc: frm.doc,
method: "set_items_for_stock_in",
callback: function(r) {
refresh_field('items');
}
});
},
refresh: function(frm) {
if(!frm.doc.docstatus) {
frm.trigger('validate_purpose_consumption');
@ -139,6 +159,16 @@ frappe.ui.form.on('Stock Entry', {
}
}
if (frm.doc.docstatus === 1
&& frm.doc.purpose == 'Stock Out') {
frm.add_custom_button(__('Make Stock In Entry'), function() {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.stock_entry.stock_entry.make_stock_in_entry",
frm: frm
})
});
}
if (frm.doc.docstatus===0) {
frm.add_custom_button(__('Purchase Invoice'), function() {
erpnext.utils.map_current_doc({
@ -457,6 +487,7 @@ frappe.ui.form.on('Stock Entry Detail', {
'voucher_no' : d.name,
'allow_zero_valuation': 1,
};
return frappe.call({
doc: frm.doc,
method: "get_item_details",
@ -721,8 +752,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
},
source_mandatory: ["Material Issue", "Material Transfer", "Subcontract", "Material Transfer for Manufacture"],
target_mandatory: ["Material Receipt", "Material Transfer", "Subcontract", "Material Transfer for Manufacture"],
source_mandatory: ["Material Issue", "Material Transfer", "Subcontract",
"Material Transfer for Manufacture", "Stock Out", "Stock In"],
target_mandatory: ["Material Receipt", "Material Transfer", "Subcontract",
"Material Transfer for Manufacture", "Stock Out", "Stock In"],
from_warehouse: function(doc) {
var me = this;
@ -787,6 +820,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
doc.purpose!='Material Issue');
this.frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue');
this.frm.toggle_reqd("outward_stock_entry",
doc.purpose == 'Stock In' ? 1: 0);
},
supplier: function(doc) {

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos, get_b
from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, add_additional_cost
from erpnext.stock.utils import get_bin
from frappe.model.mapper import get_mapped_doc
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
import json
@ -86,6 +87,7 @@ class StockEntry(StockController):
self.make_gl_entries()
self.update_cost_in_project()
self.validate_reserved_serial_no_consumption()
self.update_transferred_qty()
if self.work_order and self.purpose == "Manufacture":
self.update_so_in_serial_number()
@ -102,6 +104,7 @@ class StockEntry(StockController):
self.update_stock_ledger()
self.make_gl_entries_on_cancel()
self.update_cost_in_project()
self.update_transferred_qty()
def set_job_card_data(self):
if self.job_card and not self.work_order:
@ -118,8 +121,10 @@ class StockEntry(StockController):
frappe.throw(_("Cannot cancel transaction for Completed Work Order."))
def validate_purpose(self):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", "Material Transfer for Manufacture",
"Manufacture", "Repack", "Subcontract", "Material Consumption for Manufacture"]
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
"Material Transfer for Manufacture", "Manufacture", "Repack", "Subcontract",
"Material Consumption for Manufacture", "Stock Out", "Stock In"]
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
@ -222,8 +227,10 @@ class StockEntry(StockController):
"""perform various (sometimes conditional) validations on warehouse"""
source_mandatory = ["Material Issue", "Material Transfer", "Subcontract", "Material Transfer for Manufacture",
"Material Consumption for Manufacture"]
target_mandatory = ["Material Receipt", "Material Transfer", "Subcontract", "Material Transfer for Manufacture",]
"Material Consumption for Manufacture", "Stock Out", "Stock In"]
target_mandatory = ["Material Receipt", "Material Transfer", "Subcontract",
"Material Transfer for Manufacture", "Stock Out", "Stock In"]
validate_for_manufacture_repack = any([d.bom_no for d in self.get("items")])
@ -689,6 +696,30 @@ class StockEntry(StockController):
return ret
def set_items_for_stock_in(self):
self.items = []
if self.outward_stock_entry and self.purpose == 'Stock In':
doc = frappe.get_doc('Stock Entry', self.outward_stock_entry)
if doc.per_transferred == 100:
frappe.throw(_("Goods are already received against the outward entry {0}")
.format(doc.name))
for d in doc.items:
self.append('items', {
's_warehouse': d.t_warehouse,
'item_code': d.item_code,
'qty': d.qty,
'uom': d.uom,
'against_stock_entry': d.parent,
'ste_detail': d.name,
'stock_uom': d.stock_uom,
'conversion_factor': d.conversion_factor,
'serial_no': d.serial_no,
'batch_no': d.batch_no
})
def get_items(self):
self.set('items', [])
self.validate_work_order()
@ -1135,6 +1166,52 @@ class StockEntry(StockController):
frappe.throw(_("Item {0} (Serial No: {1}) cannot be consumed as is reserverd\
to fullfill Sales Order {2}.").format(item.item_code, sr, sales_order))
def update_transferred_qty(self):
if self.purpose == 'Stock In':
stock_entries = {}
stock_entries_child_list = []
for d in self.items:
if not (d.against_stock_entry and d.ste_detail):
continue
stock_entries_child_list.append(d.ste_detail)
transferred_qty = frappe.get_all("Stock Entry Detail", fields = ["sum(qty) as qty"],
filters = { 'against_stock_entry': d.against_stock_entry,
'ste_detail': d.ste_detail,'docstatus': 1})
stock_entries[(d.against_stock_entry, d.ste_detail)] = (transferred_qty[0].qty
if transferred_qty and transferred_qty[0] else 0.0) or 0.0
if not stock_entries: return None
cond = ''
for data, transferred_qty in stock_entries.items():
cond += """ WHEN (parent = %s and name = %s) THEN %s
""" %(frappe.db.escape(data[0]), frappe.db.escape(data[1]), transferred_qty)
if cond and stock_entries_child_list:
frappe.db.sql(""" UPDATE `tabStock Entry Detail`
SET
transferred_qty = CASE {cond} END
WHERE
name in ({ste_details}) """.format(cond=cond,
ste_details = ','.join(['%s'] * len(stock_entries_child_list))),
tuple(stock_entries_child_list))
args = {
'source_dt': 'Stock Entry Detail',
'target_field': 'transferred_qty',
'target_ref_field': 'qty',
'target_dt': 'Stock Entry Detail',
'join_field': 'ste_detail',
'target_parent_dt': 'Stock Entry',
'target_parent_field': 'per_transferred',
'source_field': 'qty',
'percent_join_field': 'against_stock_entry'
}
self._update_percent_field_in_targets(args, update_modified=True)
@frappe.whitelist()
def move_sample_to_retention_warehouse(company, items):
if isinstance(items, string_types):
@ -1171,6 +1248,44 @@ def move_sample_to_retention_warehouse(company, items):
if stock_entry.get('items'):
return stock_entry.as_dict()
@frappe.whitelist()
def make_stock_in_entry(source_name, target_doc=None):
def set_missing_values(source, target):
target.purpose = 'Stock In'
target.stock_entry_type = ''
target.set_stock_entry_type()
def update_item(source_doc, target_doc, source_parent):
target_doc.t_warehouse = ''
target_doc.s_warehouse = source_doc.t_warehouse
target_doc.qty = source_doc.qty - source_doc.transferred_qty
doclist = get_mapped_doc("Stock Entry", source_name, {
"Stock Entry": {
"doctype": "Stock Entry",
"field_map": {
"name": "outward_stock_entry"
},
"validation": {
"docstatus": ["=", 1]
}
},
"Stock Entry Detail": {
"doctype": "Stock Entry Detail",
"field_map": {
"name": "ste_detail",
"parent": "against_stock_entry",
"serial_no": "serial_no",
"batch_no": "batch_no"
},
"postprocess": update_item,
"condition": lambda doc: flt(doc.qty) - flt(doc.transferred_qty) > 0.01
},
}, target_doc, set_missing_values)
return doclist
@frappe.whitelist()
def get_work_order_details(work_order):
work_order = frappe.get_doc("Work Order", work_order)

View File

@ -1,6 +1,23 @@
frappe.listview_settings['Stock Entry'] = {
add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`",
"`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`"],
get_indicator: function (doc) {
debugger
if (doc.docstatus === 0) {
return [__("Draft"), "red", "docstatus,=,0"];
} else if (doc.purpose == 'Stock Out' && doc.per_transferred < 100) {
// not delivered & overdue
return [__("Goods In Transit"), "grey", "per_transferred,<,100"];
} else if (doc.purpose == 'Stock Out' && doc.per_transferred == 100) {
return [__("Goods Transferred"), "green", "per_transferred,=,100"];
} else if (doc.docstatus === 2) {
return [__("Canceled"), "red", "docstatus,=,2"];
} else {
return [__("Submitted"), "blue", "docstatus,=,1"];
}
},
column_render: {
"from_warehouse": function(doc) {
var html = "";

View File

@ -1,156 +1,156 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "Prompt",
"beta": 0,
"creation": "2019-03-13 16:23:46.636769",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "Prompt",
"beta": 0,
"creation": "2019-03-13 16:23:46.636769",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Material Issue",
"fetch_if_empty": 0,
"fieldname": "purpose",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Purpose",
"length": 0,
"no_copy": 0,
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSubcontract",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Material Issue",
"fetch_if_empty": 0,
"fieldname": "purpose",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Purpose",
"length": 0,
"no_copy": 0,
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSubcontract\nStock Out\nStock In",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-13 16:28:02.326991",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Type",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-13 19:30:42.144377",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Type",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}