Merge branch 'develop' into contacts-ref

This commit is contained in:
Suraj Shetty
2019-09-02 06:32:11 +05:30
committed by GitHub
44 changed files with 3079 additions and 5223 deletions

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Buying Settings', {
// refresh: function(frm) {
// }
});

View File

@@ -1,379 +1,111 @@
{ {
"allow_copy": 0, "creation": "2013-06-25 11:04:03",
"allow_guest_to_view": 0, "description": "Settings for Buying Module",
"allow_import": 0, "doctype": "DocType",
"allow_rename": 0, "document_type": "Other",
"beta": 0, "field_order": [
"creation": "2013-06-25 11:04:03", "supp_master_name",
"custom": 0, "supplier_group",
"description": "Settings for Buying Module", "buying_price_list",
"docstatus": 0, "column_break_3",
"doctype": "DocType", "po_required",
"document_type": "Other", "pr_required",
"editable_grid": 0, "maintain_same_rate",
"allow_multiple_items",
"subcontract",
"backflush_raw_materials_of_subcontract_based_on",
"column_break_11",
"over_transfer_allowance"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "default": "Supplier Name",
"allow_in_quick_entry": 0, "fieldname": "supp_master_name",
"allow_on_submit": 0, "fieldtype": "Select",
"bold": 0, "label": "Supplier Naming By",
"collapsible": 0, "options": "Supplier Name\nNaming Series"
"columns": 0,
"default": "Supplier Name",
"fieldname": "supp_master_name",
"fieldtype": "Select",
"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": "Supplier Naming By",
"length": 0,
"no_copy": 0,
"options": "Supplier Name\nNaming Series",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supplier_group",
"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": "Default Supplier Group",
"length": 0,
"no_copy": 0,
"options": "Supplier Group",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "buying_price_list",
"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": "Default Buying Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "po_required",
"fieldtype": "Select",
"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": "Purchase Order Required",
"length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "pr_required",
"fieldtype": "Select",
"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": "Purchase Receipt Required",
"length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maintain_same_rate",
"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": "Maintain same rate throughout purchase cycle",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_multiple_items",
"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 Item to be added multiple times in a transaction",
"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,
"translatable": 0,
"unique": 0
}, },
{ {
"allow_bulk_edit": 0, "fieldname": "supplier_group",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Default Supplier Group",
"bold": 0, "options": "Supplier Group"
"collapsible": 0, },
"columns": 0,
"fieldname": "subcontract",
"fieldtype": "Section Break",
"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": "Subcontract",
"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,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "buying_price_list",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Default Buying Price List",
"bold": 0, "options": "Price List"
"collapsible": 0, },
"columns": 0, {
"default": "Material Transferred for Subcontract", "fieldname": "column_break_3",
"fieldname": "backflush_raw_materials_of_subcontract_based_on", "fieldtype": "Column Break"
"fieldtype": "Select", },
"hidden": 0, {
"ignore_user_permissions": 0, "fieldname": "po_required",
"ignore_xss_filter": 0, "fieldtype": "Select",
"in_filter": 0, "label": "Purchase Order Required",
"in_global_search": 0, "options": "No\nYes"
"in_list_view": 0, },
"in_standard_filter": 0, {
"label": "Backflush Raw Materials of Subcontract Based On", "fieldname": "pr_required",
"length": 0, "fieldtype": "Select",
"no_copy": 0, "label": "Purchase Receipt Required",
"options": "BOM\nMaterial Transferred for Subcontract", "options": "No\nYes"
"permlevel": 0, },
"precision": "", {
"print_hide": 0, "default": "0",
"print_hide_if_no_value": 0, "fieldname": "maintain_same_rate",
"read_only": 0, "fieldtype": "Check",
"remember_last_selected_value": 0, "label": "Maintain same rate throughout purchase cycle"
"report_hide": 0, },
"reqd": 0, {
"search_index": 0, "default": "0",
"set_only_once": 0, "fieldname": "allow_multiple_items",
"translatable": 0, "fieldtype": "Check",
"unique": 0 "label": "Allow Item to be added multiple times in a transaction"
},
{
"fieldname": "subcontract",
"fieldtype": "Section Break",
"label": "Subcontract"
},
{
"default": "Material Transferred for Subcontract",
"fieldname": "backflush_raw_materials_of_subcontract_based_on",
"fieldtype": "Select",
"label": "Backflush Raw Materials of Subcontract Based On",
"options": "BOM\nMaterial Transferred for Subcontract"
},
{
"depends_on": "eval:doc.backflush_raw_materials_of_subcontract_based_on == \"BOM\"",
"description": "Percentage you are allowed to transfer more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to transfer 110 units.",
"fieldname": "over_transfer_allowance",
"fieldtype": "Float",
"label": "Over Transfer Allowance (%)"
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
} }
], ],
"has_web_view": 0, "icon": "fa fa-cog",
"hide_heading": 0, "idx": 1,
"hide_toolbar": 0, "issingle": 1,
"icon": "fa fa-cog", "modified": "2019-08-20 13:13:09.055189",
"idx": 1, "modified_by": "Administrator",
"image_view": 0, "module": "Buying",
"in_create": 0, "name": "Buying Settings",
"is_submittable": 0, "owner": "Administrator",
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-31 07:52:38.062488",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
"owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"cancel": 0, "email": 1,
"create": 1, "print": 1,
"delete": 0, "read": 1,
"email": 1, "role": "System Manager",
"export": 0, "share": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ]
"quick_entry": 0, }
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBuyingSettings(unittest.TestCase):
pass

View File

@@ -477,6 +477,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
rm_item_code = rm_item_data["rm_item_code"] rm_item_code = rm_item_data["rm_item_code"]
items_dict = { items_dict = {
rm_item_code: { rm_item_code: {
"po_detail": rm_item_data.get("name"),
"item_name": rm_item_data["item_name"], "item_name": rm_item_data["item_name"],
"description": item_wh.get(rm_item_code, {}).get('description', ""), "description": item_wh.get(rm_item_code, {}).get('description', ""),
'qty': rm_item_data["qty"], 'qty': rm_item_data["qty"],

View File

@@ -1,404 +1,134 @@
{ {
"allow_copy": 0, "creation": "2013-02-22 01:27:42",
"allow_events_in_timeline": 0, "doctype": "DocType",
"allow_guest_to_view": 0, "editable_grid": 1,
"allow_import": 0, "field_order": [
"allow_rename": 0, "main_item_code",
"beta": 0, "rm_item_code",
"creation": "2013-02-22 01:27:42", "required_qty",
"custom": 0, "supplied_qty",
"docstatus": 0, "rate",
"doctype": "DocType", "amount",
"editable_grid": 1, "column_break_6",
"bom_detail_no",
"reference_name",
"conversion_factor",
"stock_uom",
"reserve_warehouse"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_in_quick_entry": 0, "fieldname": "main_item_code",
"allow_on_submit": 0, "fieldtype": "Link",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Item Code",
"columns": 2, "oldfieldname": "main_item_code",
"fieldname": "main_item_code", "oldfieldtype": "Data",
"fieldtype": "Link", "options": "Item",
"hidden": 0, "read_only": 1
"ignore_user_permissions": 0, },
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"oldfieldname": "main_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_in_quick_entry": 0, "fieldname": "rm_item_code",
"allow_on_submit": 0, "fieldtype": "Link",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Raw Material Item Code",
"columns": 2, "oldfieldname": "rm_item_code",
"fieldname": "rm_item_code", "oldfieldtype": "Data",
"fieldtype": "Link", "options": "Item",
"hidden": 0, "read_only": 1
"ignore_user_permissions": 0, },
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Raw Material Item Code",
"length": 0,
"no_copy": 0,
"oldfieldname": "rm_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_in_quick_entry": 0, "fieldname": "required_qty",
"allow_on_submit": 0, "fieldtype": "Float",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Required Qty",
"columns": 2, "oldfieldname": "required_qty",
"fieldname": "required_qty", "oldfieldtype": "Currency",
"fieldtype": "Float", "read_only": 1
"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": "Supplied Qty",
"length": 0,
"no_copy": 0,
"oldfieldname": "required_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_in_quick_entry": 0, "fieldname": "rate",
"allow_on_submit": 0, "fieldtype": "Currency",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Rate",
"columns": 2, "oldfieldname": "rate",
"fieldname": "rate", "oldfieldtype": "Currency",
"fieldtype": "Currency", "options": "Company:company:default_currency"
"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": "Rate",
"length": 0,
"no_copy": 0,
"oldfieldname": "rate",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"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,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "amount",
"allow_in_quick_entry": 0, "fieldtype": "Currency",
"allow_on_submit": 0, "label": "Amount",
"bold": 0, "oldfieldname": "amount",
"collapsible": 0, "oldfieldtype": "Currency",
"columns": 0, "options": "Company:company:default_currency",
"fieldname": "amount", "read_only": 1
"fieldtype": "Currency", },
"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": "Amount",
"length": 0,
"no_copy": 0,
"oldfieldname": "amount",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "column_break_6",
"allow_in_quick_entry": 0, "fieldtype": "Column Break"
"allow_on_submit": 0, },
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"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,
"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,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "bom_detail_no",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "label": "BOM Detail No",
"bold": 0, "oldfieldname": "bom_detail_no",
"collapsible": 0, "oldfieldtype": "Data",
"columns": 0, "read_only": 1
"fieldname": "bom_detail_no", },
"fieldtype": "Data",
"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": "BOM Detail No",
"length": 0,
"no_copy": 0,
"oldfieldname": "bom_detail_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "reference_name",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "label": "Reference Name",
"bold": 0, "oldfieldname": "reference_name",
"collapsible": 0, "oldfieldtype": "Data",
"columns": 0, "read_only": 1
"fieldname": "reference_name", },
"fieldtype": "Data",
"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": "Reference Name",
"length": 0,
"no_copy": 0,
"oldfieldname": "reference_name",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "conversion_factor",
"allow_in_quick_entry": 0, "fieldtype": "Float",
"allow_on_submit": 0, "hidden": 1,
"bold": 0, "label": "Conversion Factor",
"collapsible": 0, "oldfieldname": "conversion_factor",
"columns": 0, "oldfieldtype": "Currency",
"fieldname": "conversion_factor", "read_only": 1
"fieldtype": "Float", },
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Conversion Factor",
"length": 0,
"no_copy": 0,
"oldfieldname": "conversion_factor",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "fieldname": "stock_uom",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Stock Uom",
"bold": 0, "oldfieldname": "stock_uom",
"collapsible": 0, "oldfieldtype": "Data",
"columns": 0, "options": "UOM",
"fieldname": "stock_uom", "read_only": 1
"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": "Stock Uom",
"length": 0,
"no_copy": 0,
"oldfieldname": "stock_uom",
"oldfieldtype": "Data",
"options": "UOM",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_in_quick_entry": 0, "fieldname": "reserve_warehouse",
"allow_on_submit": 0, "fieldtype": "Link",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Reserve Warehouse",
"columns": 2, "options": "Warehouse"
"fieldname": "reserve_warehouse", },
"fieldtype": "Link", {
"hidden": 0, "fieldname": "supplied_qty",
"ignore_user_permissions": 0, "fieldtype": "Float",
"ignore_xss_filter": 0, "in_list_view": 1,
"in_filter": 0, "label": "Supplied Qty",
"in_global_search": 0, "read_only": 1
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reserve Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "hide_toolbar": 1,
"hide_heading": 0, "idx": 1,
"hide_toolbar": 1, "istable": 1,
"idx": 1, "modified": "2019-08-20 13:37:32.702068",
"image_view": 0, "modified_by": "Administrator",
"in_create": 0, "module": "Buying",
"is_submittable": 0, "name": "Purchase Order Item Supplied",
"issingle": 0, "owner": "dhanalekshmi@webnotestech.com",
"istable": 1, "permissions": []
"max_attachments": 0,
"modified": "2019-01-07 16:51:58.016007",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item Supplied",
"owner": "dhanalekshmi@webnotestech.com",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0,
"track_views": 0
} }

View File

@@ -14,6 +14,12 @@ def get_data():
"dependencies": ["Item", "Supplier"], "dependencies": ["Item", "Supplier"],
"description": _("Purchase Orders given to Suppliers."), "description": _("Purchase Orders given to Suppliers."),
}, },
{
"type": "doctype",
"name": "Purchase Invoice",
"onboard": 1,
"dependencies": ["Item", "Supplier"]
},
{ {
"type": "doctype", "type": "doctype",
"name": "Material Request", "name": "Material Request",

View File

@@ -304,12 +304,6 @@ def get_data():
"name": "Customers Without Any Sales Transactions", "name": "Customers Without Any Sales Transactions",
"doctype": "Customer" "doctype": "Customer"
}, },
{
"type": "report",
"is_query_report": True,
"name": "Sales Partners Commission",
"doctype": "Customer"
},
{ {
"type": "report", "type": "report",
"is_query_report": True, "is_query_report": True,

View File

@@ -30,6 +30,12 @@ def get_data():
"onboard": 1, "onboard": 1,
"dependencies": ["Item"], "dependencies": ["Item"],
}, },
{
"type": "doctype",
"name": "Pick List",
"onboard": 1,
"dependencies": ["Item"],
},
{ {
"type": "doctype", "type": "doctype",
"name": "Delivery Trip" "name": "Delivery Trip"
@@ -329,5 +335,5 @@ def get_data():
} }
] ]
}, },
] ]

View File

@@ -263,7 +263,7 @@ class AccountsController(TransactionBase):
if self.get("is_subcontracted"): if self.get("is_subcontracted"):
args["is_subcontracted"] = self.is_subcontracted args["is_subcontracted"] = self.is_subcontracted
ret = get_item_details(args, self) ret = get_item_details(args, self, overwrite_warehouse=False)
for fieldname, value in ret.items(): for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None: if item.meta.get_field(fieldname) and value is not None:

View File

@@ -394,8 +394,8 @@ class SellingController(StockController):
elif self.doctype == "Delivery Note": elif self.doctype == "Delivery Note":
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
elif self.doctype == "Sales Order": elif self.doctype in ["Sales Order", "Quotation"]:
e = [d.item_code, d.description, d.warehouse, d.batch_no or ''] e = [d.item_code, d.description, d.warehouse, '']
f = [d.item_code, d.description] f = [d.item_code, d.description]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:

View File

@@ -58,7 +58,7 @@ class ShopifySettings(Document):
d.raise_for_status() d.raise_for_status()
self.update_webhook_table(method, d.json()) self.update_webhook_table(method, d.json())
except Exception as e: except Exception as e:
make_shopify_log(status="Warning", message=e.message, exception=False) make_shopify_log(status="Warning", message=e, exception=False)
def unregister_webhooks(self): def unregister_webhooks(self):
session = get_request_session() session = get_request_session()
@@ -71,7 +71,7 @@ class ShopifySettings(Document):
res.raise_for_status() res.raise_for_status()
deleted_webhooks.append(d) deleted_webhooks.append(d)
except Exception as e: except Exception as e:
frappe.log_error(message=frappe.get_traceback(), title=e.message[:140]) frappe.log_error(message=frappe.get_traceback(), title=e)
for d in deleted_webhooks: for d in deleted_webhooks:
self.remove(d) self.remove(d)

View File

@@ -95,26 +95,25 @@ def process_expired_allocation():
'expire_carry_forwarded_leaves_after_days': (">", 0) 'expire_carry_forwarded_leaves_after_days': (">", 0)
}, fieldname=['name']) }, fieldname=['name'])
if leave_type_records: leave_type = [record[0] for record in leave_type_records]
leave_type = [record[0] for record in leave_type_records]
expired_allocation = frappe.db.sql_list("""SELECT name expired_allocation = frappe.db.sql_list("""SELECT name
FROM `tabLeave Ledger Entry` FROM `tabLeave Ledger Entry`
WHERE WHERE
`transaction_type`='Leave Allocation' `transaction_type`='Leave Allocation'
AND `is_expired`=1""") AND `is_expired`=1""")
expire_allocation = frappe.get_all("Leave Ledger Entry", expire_allocation = frappe.get_all("Leave Ledger Entry",
fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
filters={ filters={
'to_date': ("<", today()), 'to_date': ("<", today()),
'transaction_type': 'Leave Allocation', 'transaction_type': 'Leave Allocation',
'transaction_name': ('not in', expired_allocation) 'transaction_name': ('not in', expired_allocation)
}, },
or_filters={ or_filters={
'is_carry_forward': 0, 'is_carry_forward': 0,
'leave_type': ('in', leave_type) 'leave_type': ('in', leave_type)
}) })
if expire_allocation: if expire_allocation:
create_expiry_ledger_entry(expire_allocation) create_expiry_ledger_entry(expire_allocation)

View File

@@ -4,16 +4,17 @@
frappe.ui.form.on("Work Order", { frappe.ui.form.on("Work Order", {
setup: function(frm) { setup: function(frm) {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Stock Entry': 'Make Stock Entry', 'Stock Entry': 'Start',
} 'Pick List': 'Create Pick List',
};
// Set query for warehouses // Set query for warehouses
frm.set_query("wip_warehouse", function(doc) { frm.set_query("wip_warehouse", function() {
return { return {
filters: { filters: {
'company': frm.doc.company, 'company': frm.doc.company,
} }
} };
}); });
frm.set_query("source_warehouse", function() { frm.set_query("source_warehouse", function() {
@@ -21,7 +22,7 @@ frappe.ui.form.on("Work Order", {
filters: { filters: {
'company': frm.doc.company, 'company': frm.doc.company,
} }
} };
}); });
frm.set_query("source_warehouse", "required_items", function() { frm.set_query("source_warehouse", "required_items", function() {
@@ -29,7 +30,7 @@ frappe.ui.form.on("Work Order", {
filters: { filters: {
'company': frm.doc.company, 'company': frm.doc.company,
} }
} };
}); });
frm.set_query("sales_order", function() { frm.set_query("sales_order", function() {
@@ -37,7 +38,7 @@ frappe.ui.form.on("Work Order", {
filters: { filters: {
"status": ["not in", ["Closed", "On Hold"]] "status": ["not in", ["Closed", "On Hold"]]
} }
} };
}); });
frm.set_query("fg_warehouse", function() { frm.set_query("fg_warehouse", function() {
@@ -46,7 +47,7 @@ frappe.ui.form.on("Work Order", {
'company': frm.doc.company, 'company': frm.doc.company,
'is_group': 0 'is_group': 0
} }
} };
}); });
frm.set_query("scrap_warehouse", function() { frm.set_query("scrap_warehouse", function() {
@@ -55,17 +56,19 @@ frappe.ui.form.on("Work Order", {
'company': frm.doc.company, 'company': frm.doc.company,
'is_group': 0 'is_group': 0
} }
} };
}); });
// Set query for BOM // Set query for BOM
frm.set_query("bom_no", function() { frm.set_query("bom_no", function() {
if (frm.doc.production_item) { if (frm.doc.production_item) {
return{ return {
query: "erpnext.controllers.queries.bom", query: "erpnext.controllers.queries.bom",
filters: {item: cstr(frm.doc.production_item)} filters: {item: cstr(frm.doc.production_item)}
} };
} else msgprint(__("Please enter Production Item first")); } else {
frappe.msgprint(__("Please enter Production Item first"));
}
}); });
// Set query for FG Item // Set query for FG Item
@@ -76,7 +79,7 @@ frappe.ui.form.on("Work Order", {
['is_stock_item', '=',1], ['is_stock_item', '=',1],
['default_bom', '!=', ''] ['default_bom', '!=', '']
] ]
} };
}); });
// Set query for FG Item // Set query for FG Item
@@ -85,12 +88,12 @@ frappe.ui.form.on("Work Order", {
filters:[ filters:[
['Project', 'status', 'not in', 'Completed, Cancelled'] ['Project', 'status', 'not in', 'Completed, Cancelled']
] ]
} };
}); });
// formatter for work order operation // formatter for work order operation
frm.set_indicator_formatter('operation', frm.set_indicator_formatter('operation',
function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" }); function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange"; });
}, },
onload: function(frm) { onload: function(frm) {
@@ -133,7 +136,7 @@ frappe.ui.form.on("Work Order", {
if(not_completed && not_completed.length) { if(not_completed && not_completed.length) {
frm.add_custom_button(__('Create Job Card'), () => { frm.add_custom_button(__('Create Job Card'), () => {
frm.trigger("make_job_card") frm.trigger("make_job_card");
}).addClass('btn-primary'); }).addClass('btn-primary');
} }
} }
@@ -151,7 +154,7 @@ frappe.ui.form.on("Work Order", {
condition: (d) => { condition: (d) => {
if (d.allow_alternative_item) {return true;} if (d.allow_alternative_item) {return true;}
} }
}) });
}); });
} }
} }
@@ -285,13 +288,13 @@ frappe.ui.form.on("Work Order", {
if(!frm.doc.skip_transfer){ if(!frm.doc.skip_transfer){
var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty; var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty;
if(pending_complete) { if(pending_complete) {
var title = __('{0} items in progress', [pending_complete]);
var width = ((pending_complete / frm.doc.qty * 100) - added_min); var width = ((pending_complete / frm.doc.qty * 100) - added_min);
title = __('{0} items in progress', [pending_complete]);
bars.push({ bars.push({
'title': title, 'title': title,
'width': (width > 100 ? "99.5" : width) + '%', 'width': (width > 100 ? "99.5" : width) + '%',
'progress_class': 'progress-bar-warning' 'progress_class': 'progress-bar-warning'
}) });
message = message + '. ' + title; message = message + '. ' + title;
} }
} }
@@ -377,7 +380,7 @@ frappe.ui.form.on("Work Order", {
filters: [ filters: [
["Sales Order","name", "in", r.message] ["Sales Order","name", "in", r.message]
] ]
} };
}); });
} }
}); });
@@ -401,10 +404,10 @@ frappe.ui.form.on("Work Order Item", {
frappe.model.set_value(row.doctype, row.name, frappe.model.set_value(row.doctype, row.name,
"available_qty_at_source_warehouse", r.message); "available_qty_at_source_warehouse", r.message);
} }
}) });
} }
} }
}) });
frappe.ui.form.on("Work Order Operation", { frappe.ui.form.on("Work Order Operation", {
workstation: function(frm, cdt, cdn) { workstation: function(frm, cdt, cdn) {
@@ -421,7 +424,7 @@ frappe.ui.form.on("Work Order Operation", {
erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_cost(frm.doc);
erpnext.work_order.calculate_total_cost(frm); erpnext.work_order.calculate_total_cost(frm);
} }
}) });
} }
}, },
time_in_mins: function(frm, cdt, cdn) { time_in_mins: function(frm, cdt, cdn) {
@@ -447,10 +450,13 @@ erpnext.work_order = {
const show_start_btn = (frm.doc.skip_transfer const show_start_btn = (frm.doc.skip_transfer
|| frm.doc.transfer_material_against == 'Job Card') ? 0 : 1; || frm.doc.transfer_material_against == 'Job Card') ? 0 : 1;
if (show_start_btn){ if (show_start_btn) {
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty)) if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
&& frm.doc.status != 'Stopped') { && frm.doc.status != 'Stopped') {
frm.has_start_btn = true; frm.has_start_btn = true;
frm.add_custom_button(__('Create Pick List'), function() {
erpnext.work_order.create_pick_list(frm);
});
var start_btn = frm.add_custom_button(__('Start'), function() { var start_btn = frm.add_custom_button(__('Start'), function() {
erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture'); erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture');
}); });
@@ -519,8 +525,8 @@ erpnext.work_order = {
calculate_total_cost: function(frm) { calculate_total_cost: function(frm) {
var variable_cost = frm.doc.actual_operating_cost ? var variable_cost = frm.doc.actual_operating_cost ?
flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost) flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost);
frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)) frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost));
}, },
set_default_warehouse: function(frm) { set_default_warehouse: function(frm) {
@@ -528,45 +534,72 @@ erpnext.work_order = {
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse", method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse",
callback: function(r) { callback: function(r) {
if(!r.exe) { if (!r.exe) {
frm.set_value("wip_warehouse", r.message.wip_warehouse); frm.set_value("wip_warehouse", r.message.wip_warehouse);
frm.set_value("fg_warehouse", r.message.fg_warehouse) frm.set_value("fg_warehouse", r.message.fg_warehouse);
} }
} }
}); });
} }
}, },
make_se: function(frm, purpose) { get_max_transferable_qty: (frm, purpose) => {
if(!frm.doc.skip_transfer){ let max = 0;
var max = (purpose === "Manufacture") ? if (frm.doc.skip_transfer) return max;
flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) : if (purpose === 'Manufacture') {
flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty);
} else { } else {
var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
} }
return flt(max, precision('qty'));
},
max = flt(max, precision("qty")); show_prompt_for_qty_input: function(frm, purpose) {
frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty", let max = this.get_max_transferable_qty(frm, purpose);
description: __("Max: {0}", [max]), 'default': max }, function(data) return new Promise((resolve, reject) => {
{ frappe.prompt({
if(data.qty > max) { fieldtype: 'Float',
frappe.msgprint(__("Quantity must not be more than {0}", [max])); label: __('Qty for {0}', [purpose]),
return; fieldname: 'qty',
} description: __('Max: {0}', [max]),
frappe.call({ default: max
method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", }, data => {
args: { if (data.qty > max) {
"work_order_id": frm.doc.name, frappe.msgprint(__('Quantity must not be more than {0}', [max]));
"purpose": purpose, reject();
"qty": data.qty
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
} }
data.purpose = purpose;
resolve(data);
}, __('Select Quantity'), __('Create'));
});
},
make_se: function(frm, purpose) {
this.show_prompt_for_qty_input(frm, purpose)
.then(data => {
return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', {
'work_order_id': frm.doc.name,
'purpose': purpose,
'qty': data.qty
});
}).then(stock_entry => {
frappe.model.sync(stock_entry);
frappe.set_route('Form', stock_entry.doctype, stock_entry.name);
});
},
create_pick_list: function(frm, purpose='Material Transfer for Manufacture') {
this.show_prompt_for_qty_input(frm, purpose)
.then(data => {
return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', {
'source_name': frm.doc.name,
'for_qty': data.qty
});
}).then(pick_list => {
frappe.model.sync(pick_list);
frappe.set_route('Form', pick_list.doctype, pick_list.name);
}); });
}, __("Select Quantity"), __('Create'));
}, },
make_consumption_se: function(frm, backflush_raw_materials_based_on) { make_consumption_se: function(frm, backflush_raw_materials_based_on) {
@@ -606,6 +639,6 @@ erpnext.work_order = {
frm.reload_doc(); frm.reload_doc();
} }
} }
}) });
} }
} };

View File

@@ -72,6 +72,7 @@
"fieldname": "naming_series", "fieldname": "naming_series",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Series", "label": "Series",
"no_copy": 1,
"options": "MFG-WO-.YYYY.-", "options": "MFG-WO-.YYYY.-",
"print_hide": 1, "print_hide": 1,
"reqd": 1, "reqd": 1,
@@ -467,7 +468,7 @@
"idx": 1, "idx": 1,
"image_field": "image", "image_field": "image",
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-07-31 00:13:38.218277", "modified": "2019-08-28 12:29:35.315239",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order", "name": "Work Order",

View File

@@ -19,6 +19,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from frappe.utils.csvutils import getlink from frappe.utils.csvutils import getlink
from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
from erpnext.utilities.transaction_base import validate_uom_is_integer from erpnext.utilities.transaction_base import validate_uom_is_integer
from frappe.model.mapper import get_mapped_doc
class OverProductionError(frappe.ValidationError): pass class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass
@@ -707,3 +708,46 @@ def get_work_order_operation_data(work_order, operation, workstation):
for d in work_order.operations: for d in work_order.operations:
if d.operation == operation and d.workstation == workstation: if d.operation == operation and d.workstation == workstation:
return d return d
@frappe.whitelist()
def create_pick_list(source_name, target_doc=None, for_qty=None):
for_qty = for_qty or json.loads(target_doc).get('for_qty')
max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty')
def update_item_quantity(source, target, source_parent):
pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty)
desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty)
qty = 0
if desire_to_transfer <= pending_to_issue:
qty = desire_to_transfer
elif pending_to_issue > 0:
qty = pending_to_issue
if qty:
target.qty = qty
target.stock_qty = qty
target.uom = frappe.get_value('Item', source.item_code, 'stock_uom')
target.stock_uom = target.uom
target.conversion_factor = 1
else:
target.delete()
doc = get_mapped_doc('Work Order', source_name, {
'Work Order': {
'doctype': 'Pick List',
'validation': {
'docstatus': ['=', 1]
}
},
'Work Order Item': {
'doctype': 'Pick List Item',
'postprocess': update_item_quantity,
'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty)
},
}, target_doc)
doc.for_qty = for_qty
doc.set_item_locations()
return doc

View File

@@ -6,7 +6,7 @@ def get_data():
'fieldname': 'work_order', 'fieldname': 'work_order',
'transactions': [ 'transactions': [
{ {
'items': ['Stock Entry', 'Job Card'] 'items': ['Pick List', 'Stock Entry', 'Job Card']
} }
] ]
} }

View File

@@ -625,5 +625,10 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.update_ewaybill_field_position
erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link')
execute:frappe.reload_doc('desk', 'doctype','dashboard')
execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source')
execute:frappe.reload_doc('desk', 'doctype','dashboard_chart')
erpnext.patches.v12_0.add_default_dashboards
erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.remove_bank_remittance_custom_fields
erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v12_0.generate_leave_ledger_entries

View File

@@ -0,0 +1,8 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards
def execute():
add_dashboards()

View File

@@ -7,6 +7,7 @@ frappe.ui.form.on("Sales Order", {
setup: function(frm) { setup: function(frm) {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Delivery Note': 'Delivery', 'Delivery Note': 'Delivery',
'Pick List': 'Pick List',
'Sales Invoice': 'Invoice', 'Sales Invoice': 'Invoice',
'Material Request': 'Material Request', 'Material Request': 'Material Request',
'Purchase Order': 'Purchase Order', 'Purchase Order': 'Purchase Order',
@@ -109,7 +110,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
this._super(); this._super();
let allow_delivery = false; let allow_delivery = false;
if(doc.docstatus==1) { if (doc.docstatus==1) {
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
if(this.frm.has_perm("submit")) { if(this.frm.has_perm("submit")) {
if(doc.status === 'On Hold') { if(doc.status === 'On Hold') {
// un-hold // un-hold
@@ -233,6 +236,13 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
this.order_type(doc); this.order_type(doc);
}, },
create_pick_list() {
frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.create_pick_list",
frm: this.frm
})
},
make_work_order() { make_work_order() {
var me = this; var me = this;
this.frm.call({ this.frm.call({

View File

@@ -559,7 +559,7 @@ def make_project(source_name, target_doc=None):
return doc return doc
@frappe.whitelist() @frappe.whitelist()
def make_delivery_note(source_name, target_doc=None): def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False):
def set_missing_values(source, target): def set_missing_values(source, target):
target.ignore_pricing_rule = 1 target.ignore_pricing_rule = 1
target.run_method("set_missing_values") target.run_method("set_missing_values")
@@ -584,23 +584,13 @@ def make_delivery_note(source_name, target_doc=None):
or item.get("buying_cost_center") \ or item.get("buying_cost_center") \
or item_group.get("buying_cost_center") or item_group.get("buying_cost_center")
target_doc = get_mapped_doc("Sales Order", source_name, { mapper = {
"Sales Order": { "Sales Order": {
"doctype": "Delivery Note", "doctype": "Delivery Note",
"validation": { "validation": {
"docstatus": ["=", 1] "docstatus": ["=", 1]
} }
}, },
"Sales Order Item": {
"doctype": "Delivery Note Item",
"field_map": {
"rate": "rate",
"name": "so_detail",
"parent": "against_sales_order",
},
"postprocess": update_item,
"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
},
"Sales Taxes and Charges": { "Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"add_if_empty": True "add_if_empty": True
@@ -609,7 +599,21 @@ def make_delivery_note(source_name, target_doc=None):
"doctype": "Sales Team", "doctype": "Sales Team",
"add_if_empty": True "add_if_empty": True
} }
}, target_doc, set_missing_values) }
if not skip_item_mapping:
mapper["Sales Order Item"] = {
"doctype": "Delivery Note Item",
"field_map": {
"rate": "rate",
"name": "so_detail",
"parent": "against_sales_order",
},
"postprocess": update_item,
"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
}
target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values)
return target_doc return target_doc
@@ -987,3 +991,33 @@ def make_raw_material_request(items, company, sales_order, project=None):
def make_inter_company_purchase_order(source_name, target_doc=None): def make_inter_company_purchase_order(source_name, target_doc=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
return make_inter_company_transaction("Sales Order", source_name, target_doc) return make_inter_company_transaction("Sales Order", source_name, target_doc)
@frappe.whitelist()
def create_pick_list(source_name, target_doc=None):
def update_item_quantity(source, target, source_parent):
target.qty = flt(source.qty) - flt(source.delivered_qty)
target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
doc = get_mapped_doc('Sales Order', source_name, {
'Sales Order': {
'doctype': 'Pick List',
'validation': {
'docstatus': ['=', 1]
}
},
'Sales Order Item': {
'doctype': 'Pick List Item',
'field_map': {
'parent': 'sales_order',
'name': 'sales_order_item'
},
'postprocess': update_item_quantity,
'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
},
}, target_doc)
doc.purpose = 'Delivery against Sales Order'
doc.set_item_locations()
return doc

View File

@@ -17,7 +17,7 @@ def get_data():
'transactions': [ 'transactions': [
{ {
'label': _('Fulfillment'), 'label': _('Fulfillment'),
'items': ['Sales Invoice', 'Delivery Note'] 'items': ['Sales Invoice', 'Pick List', 'Delivery Note']
}, },
{ {
'label': _('Purchasing'), 'label': _('Purchasing'),

View File

@@ -0,0 +1,107 @@
from __future__ import unicode_literals
from frappe import _
import frappe
import json
def get_default_dashboards():
company = frappe.get_doc("Company", frappe.defaults.get_defaults().company)
income_account = company.default_income_account or get_account("Income Account", company.name)
expense_account = company.default_expense_account or get_account("Expense Account", company.name)
bank_account = company.default_bank_account or get_account("Bank", company.name)
return {
"Dashboards": [
{
"doctype": "Dashboard",
"dashboard_name": "Accounts",
"charts": [
{ "chart": "Outgoing Bills (Sales Invoice)" },
{ "chart": "Incoming Bills (Purchase Invoice)" },
{ "chart": "Bank Balance" },
{ "chart": "Income" },
{ "chart": "Expenses" }
]
}
],
"Charts": [
{
"doctype": "Dashboard Chart",
"time_interval": "Quarterly",
"chart_name": "Income",
"timespan": "Last Year",
"color": None,
"filters_json": json.dumps({"company": company.name, "account": income_account}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
},
{
"doctype": "Dashboard Chart",
"time_interval": "Quarterly",
"chart_name": "Expenses",
"timespan": "Last Year",
"color": None,
"filters_json": json.dumps({"company": company.name, "account": expense_account}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
},
{
"doctype": "Dashboard Chart",
"time_interval": "Quarterly",
"chart_name": "Bank Balance",
"timespan": "Last Year",
"color": "#ffb868",
"filters_json": json.dumps({"company": company.name, "account": bank_account}),
"source": "Account Balance Timeline",
"chart_type": "Custom",
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
},
{
"doctype": "Dashboard Chart",
"time_interval": "Monthly",
"chart_name": "Incoming Bills (Purchase Invoice)",
"timespan": "Last Year",
"color": "#a83333",
"value_based_on": "base_grand_total",
"filters_json": json.dumps({}),
"chart_type": "Sum",
"timeseries": 1,
"based_on": "posting_date",
"owner": "Administrator",
"document_type": "Purchase Invoice",
"type": "Bar",
"width": "Half"
},
{
"doctype": "Dashboard Chart",
"time_interval": "Monthly",
"chart_name": "Outgoing Bills (Sales Invoice)",
"timespan": "Last Year",
"color": "#7b933d",
"value_based_on": "base_grand_total",
"filters_json": json.dumps({}),
"chart_type": "Sum",
"timeseries": 1,
"based_on": "posting_date",
"owner": "Administrator",
"document_type": "Sales Invoice",
"type": "Bar",
"width": "Half"
}
]
}
def get_account(account_type, company):
accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
if accounts:
return accounts[0].name

View File

@@ -475,13 +475,14 @@ def install_defaults(args=None):
frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False) frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
return doc
except RootNotEditable: except RootNotEditable:
frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account)) frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
# bank account same as a CoA entry # bank account same as a CoA entry
pass pass
add_dashboards()
# Now, with fixtures out of the way, onto concrete stuff # Now, with fixtures out of the way, onto concrete stuff
records = [ records = [
@@ -499,6 +500,13 @@ def install_defaults(args=None):
make_records(records) make_records(records)
def add_dashboards():
from erpnext.setup.setup_wizard.data.dashboard_charts import get_default_dashboards
dashboard_data = get_default_dashboards()
make_records(dashboard_data["Charts"])
make_records(dashboard_data["Dashboards"])
def get_fy_details(fy_start_date, fy_end_date): def get_fy_details(fy_start_date, fy_end_date):
start_year = getdate(fy_start_date).year start_year = getdate(fy_start_date).year

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ frappe.ui.form.on('Material Request', {
setup: function(frm) { setup: function(frm) {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Stock Entry': 'Issue Material', 'Stock Entry': 'Issue Material',
'Pick List': 'Pick List',
'Purchase Order': 'Purchase Order', 'Purchase Order': 'Purchase Order',
'Request for Quotation': 'Request for Quotation', 'Request for Quotation': 'Request for Quotation',
'Supplier Quotation': 'Supplier Quotation', 'Supplier Quotation': 'Supplier Quotation',
@@ -55,8 +56,13 @@ frappe.ui.form.on('Material Request', {
if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') { if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') {
if (flt(frm.doc.per_ordered, 2) < 100) { if (flt(frm.doc.per_ordered, 2) < 100) {
// make let add_create_pick_list_button = () => {
frm.add_custom_button(__('Pick List'),
() => frm.events.create_pick_list(frm), __('Create'));
}
if (frm.doc.material_request_type === "Material Transfer") { if (frm.doc.material_request_type === "Material Transfer") {
add_create_pick_list_button();
frm.add_custom_button(__("Transfer Material"), frm.add_custom_button(__("Transfer Material"),
() => frm.events.make_stock_entry(frm), __('Create')); () => frm.events.make_stock_entry(frm), __('Create'));
} }
@@ -258,6 +264,13 @@ frappe.ui.form.on('Material Request', {
}); });
}, },
create_pick_list: (frm) => {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.material_request.material_request.create_pick_list",
frm: frm
});
},
raise_work_orders: function(frm) { raise_work_orders: function(frm) {
frappe.call({ frappe.call({
method:"erpnext.stock.doctype.material_request.material_request.raise_work_orders", method:"erpnext.stock.doctype.material_request.material_request.raise_work_orders",

View File

@@ -502,3 +502,28 @@ def raise_work_orders(material_request):
frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors)) frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors))
return work_orders return work_orders
@frappe.whitelist()
def create_pick_list(source_name, target_doc=None):
doc = get_mapped_doc('Material Request', source_name, {
'Material Request': {
'doctype': 'Pick List',
'field_map': {
'material_request_type': 'purpose'
},
'validation': {
'docstatus': ['=', 1]
}
},
'Material Request Item': {
'doctype': 'Pick List Item',
'field_map': {
'name': 'material_request_item',
'qty': 'stock_qty'
},
},
}, target_doc)
doc.set_item_locations()
return doc

View File

@@ -8,7 +8,7 @@ def get_data():
'transactions': [ 'transactions': [
{ {
'label': _('Related'), 'label': _('Related'),
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', "Stock Entry"] 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', 'Stock Entry', 'Pick List']
}, },
{ {
'label': _('Manufacturing'), 'label': _('Manufacturing'),

View File

@@ -0,0 +1,180 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Pick List', {
setup: (frm) => {
frm.custom_make_buttons = {
'Delivery Note': 'Delivery Note',
'Stock Entry': 'Stock Entry',
};
frm.set_query('parent_warehouse', () => {
return {
filters: {
'is_group': 1,
'company': frm.doc.company
}
};
});
frm.set_query('work_order', () => {
return {
query: 'erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders',
filters: {
'company': frm.doc.company
}
};
});
frm.set_query('material_request', () => {
return {
filters: {
'material_request_type': ['=', frm.doc.purpose]
}
};
});
frm.set_query('item_code', 'locations', () => {
return {
filters: {
is_stock_item: 1
}
};
});
},
get_item_locations: (frm) => {
if (!frm.doc.locations || !frm.doc.locations.length) {
frappe.msgprint(__('First add items in the Item Locations table'));
} else {
frm.call('set_item_locations');
}
},
refresh: (frm) => {
frm.trigger('add_get_items_button');
if (frm.doc.docstatus === 1) {
frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.target_document_exists', {
'pick_list_name': frm.doc.name,
'purpose': frm.doc.purpose
}).then(target_document_exists => {
if (target_document_exists) return;
if (frm.doc.purpose === 'Delivery against Sales Order') {
frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create'));
} else {
frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create'));
}
});
}
},
work_order: (frm) => {
frappe.db.get_value('Work Order',
frm.doc.work_order,
['qty', 'material_transferred_for_manufacturing']
).then(data => {
let qty_data = data.message;
let max = qty_data.qty - qty_data.material_transferred_for_manufacturing;
frappe.prompt({
fieldtype: 'Float',
label: __('Qty of Finished Goods Item'),
fieldname: 'qty',
description: __('Max: {0}', [max]),
default: max
}, (data) => {
frm.set_value('for_qty', data.qty);
if (data.qty > max) {
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
return;
}
frm.clear_table('locations');
erpnext.utils.map_current_doc({
method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list',
target: frm,
source_name: frm.doc.work_order
});
}, __('Select Quantity'), __('Get Items'));
});
},
material_request: (frm) => {
erpnext.utils.map_current_doc({
method: 'erpnext.stock.doctype.material_request.material_request.create_pick_list',
target: frm,
source_name: frm.doc.material_request
});
},
purpose: (frm) => {
frm.clear_table('locations');
frm.trigger('add_get_items_button');
},
create_delivery_note: (frm) => {
frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note',
frm: frm
});
},
create_stock_entry: (frm) => {
frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', {
'pick_list': frm.doc,
}).then(stock_entry => {
frappe.model.sync(stock_entry);
frappe.set_route("Form", 'Stock Entry', stock_entry.name);
});
},
add_get_items_button: (frm) => {
let purpose = frm.doc.purpose;
if (purpose != 'Delivery against Sales Order' || frm.doc.docstatus !== 0) return;
let get_query_filters = {
docstatus: 1,
per_delivered: ['<', 100],
status: ['!=', ''],
customer: frm.doc.customer
};
frm.get_items_btn = frm.add_custom_button(__('Get Items'), () => {
if (!frm.doc.customer) {
frappe.msgprint(__('Please select Customer first'));
return;
}
erpnext.utils.map_current_doc({
method: 'erpnext.selling.doctype.sales_order.sales_order.create_pick_list',
source_doctype: 'Sales Order',
target: frm,
setters: {
company: frm.doc.company,
customer: frm.doc.customer
},
date_field: 'transaction_date',
get_query_filters: get_query_filters
});
});
}
});
frappe.ui.form.on('Pick List Item', {
item_code: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
if (row.item_code) {
get_item_details(row.item_code).then(data => {
frappe.model.set_value(cdt, cdn, 'uom', data.stock_uom);
frappe.model.set_value(cdt, cdn, 'stock_uom', data.stock_uom);
frappe.model.set_value(cdt, cdn, 'conversion_factor', 1);
});
}
},
uom: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
if (row.uom) {
get_item_details(row.item_code, row.uom).then(data => {
frappe.model.set_value(cdt, cdn, 'conversion_factor', data.conversion_factor);
});
}
},
qty: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor);
},
conversion_factor: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor);
}
});
function get_item_details(item_code, uom=null) {
return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', {
item_code,
uom
});
}

View File

@@ -0,0 +1,184 @@
{
"autoname": "naming_series:",
"creation": "2019-07-11 16:03:13.681045",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"company",
"purpose",
"customer",
"work_order",
"material_request",
"for_qty",
"column_break_4",
"parent_warehouse",
"get_item_locations",
"section_break_6",
"locations",
"amended_from"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_6",
"fieldtype": "Section Break"
},
{
"description": "Items under this warehouse will be suggested",
"fieldname": "parent_warehouse",
"fieldtype": "Link",
"label": "Parent Warehouse",
"options": "Warehouse"
},
{
"depends_on": "eval:doc.purpose==='Delivery against Sales Order'",
"fieldname": "customer",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer"
},
{
"depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'",
"fieldname": "work_order",
"fieldtype": "Link",
"label": "Work Order",
"options": "Work Order"
},
{
"fieldname": "locations",
"fieldtype": "Table",
"label": "Item Locations",
"options": "Pick List Item"
},
{
"depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'",
"description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item",
"fieldname": "for_qty",
"fieldtype": "Float",
"label": "Qty of Finished Goods Item",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Pick List",
"print_hide": 1,
"read_only": 1
},
{
"default": "Material Transfer for Manufacture",
"fieldname": "purpose",
"fieldtype": "Select",
"label": "Purpose",
"options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery against Sales Order"
},
{
"depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)",
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
"options": "Material Request"
},
{
"depends_on": "eval:doc.docstatus===0",
"fieldname": "get_item_locations",
"fieldtype": "Button",
"label": "Get Item Locations"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "STO-PICK-.YYYY.-",
"reqd": 1,
"set_only_once": 1
}
],
"is_submittable": 1,
"modified": "2019-08-29 21:10:11.572387",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock User",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"share": 1,
"submit": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,432 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
from six import iteritems
from frappe.model.document import Document
from frappe import _
from collections import OrderedDict
from frappe.utils import floor, flt, today, cint
from frappe.model.mapper import get_mapped_doc, map_child_doc
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order
# TODO: Prioritize SO or WO group warehouse
class PickList(Document):
def before_save(self):
self.set_item_locations()
def before_submit(self):
for item in self.locations:
if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
continue
if len(item.serial_no.split('\n')) == item.picked_qty:
continue
frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
.format(frappe.bold(item.item_code), frappe.bold(item.idx)))
def set_item_locations(self):
items = self.aggregate_item_qty()
self.item_location_map = frappe._dict()
from_warehouses = None
if self.parent_warehouse:
from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse)
# reset
self.delete_key('locations')
for item_doc in items:
item_code = item_doc.item_code
self.item_location_map.setdefault(item_code,
get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code)))
locations = get_items_with_location_and_quantity(item_doc, self.item_location_map)
item_doc.idx = None
item_doc.name = None
for row in locations:
row.update({
'picked_qty': row.stock_qty
})
location = item_doc.as_dict()
location.update(row)
self.append('locations', location)
def aggregate_item_qty(self):
locations = self.get('locations')
self.item_count_map = {}
# aggregate qty for same item
item_map = OrderedDict()
for item in locations:
item_code = item.item_code
reference = item.sales_order_item or item.material_request_item
key = (item_code, item.uom, reference)
item.idx = None
item.name = None
if item_map.get(key):
item_map[key].qty += item.qty
item_map[key].stock_qty += item.stock_qty
else:
item_map[key] = item
# maintain count of each item (useful to limit get query)
self.item_count_map.setdefault(item_code, 0)
self.item_count_map[item_code] += item.stock_qty
return item_map.values()
def get_items_with_location_and_quantity(item_doc, item_location_map):
available_locations = item_location_map.get(item_doc.item_code)
locations = []
remaining_stock_qty = item_doc.stock_qty
while remaining_stock_qty > 0 and available_locations:
item_location = available_locations.pop(0)
item_location = frappe._dict(item_location)
stock_qty = remaining_stock_qty if item_location.qty >= remaining_stock_qty else item_location.qty
qty = stock_qty / (item_doc.conversion_factor or 1)
uom_must_be_whole_number = frappe.db.get_value('UOM', item_doc.uom, 'must_be_whole_number')
if uom_must_be_whole_number:
qty = floor(qty)
stock_qty = qty * item_doc.conversion_factor
if not stock_qty: break
serial_nos = None
if item_location.serial_no:
serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)])
locations.append(frappe._dict({
'qty': qty,
'stock_qty': stock_qty,
'warehouse': item_location.warehouse,
'serial_no': serial_nos,
'batch_no': item_location.batch_no
}))
remaining_stock_qty -= stock_qty
qty_diff = item_location.qty - stock_qty
# if extra quantity is available push current warehouse to available locations
if qty_diff > 0:
item_location.qty = qty_diff
if item_location.serial_no:
# set remaining serial numbers
item_location.serial_no = item_location.serial_no[-qty_diff:]
available_locations = [item_location] + available_locations
# update available locations for the item
item_location_map[item_doc.item_code] = available_locations
return locations
def get_available_item_locations(item_code, from_warehouses, required_qty):
locations = []
if frappe.get_cached_value('Item', item_code, 'has_serial_no'):
locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty)
elif frappe.get_cached_value('Item', item_code, 'has_batch_no'):
locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty)
else:
locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty)
total_qty_available = sum(location.get('qty') for location in locations)
remaining_qty = required_qty - total_qty_available
if remaining_qty > 0:
frappe.msgprint(_('{0} units of {1} is not available.')
.format(remaining_qty, frappe.get_desk_link('Item', item_code)))
return locations
def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty):
filters = frappe._dict({
'item_code': item_code,
'warehouse': ['!=', '']
})
if from_warehouses:
filters.warehouse = ['in', from_warehouses]
serial_nos = frappe.get_all('Serial No',
fields=['name', 'warehouse'],
filters=filters,
limit=required_qty,
order_by='purchase_date',
as_list=1)
warehouse_serial_nos_map = frappe._dict()
for serial_no, warehouse in serial_nos:
warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no)
locations = []
for warehouse, serial_nos in iteritems(warehouse_serial_nos_map):
locations.append({
'qty': len(serial_nos),
'warehouse': warehouse,
'serial_no': serial_nos
})
return locations
def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty):
warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else ''
batch_locations = frappe.db.sql("""
SELECT
sle.`warehouse`,
sle.`batch_no`,
SUM(sle.`actual_qty`) AS `qty`
FROM
`tabStock Ledger Entry` sle, `tabBatch` batch
WHERE
sle.batch_no = batch.name
and sle.`item_code`=%(item_code)s
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
{warehouse_condition}
GROUP BY
`warehouse`,
`batch_no`,
`item_code`
HAVING `qty` > 0
ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`
""".format(warehouse_condition=warehouse_condition), { #nosec
'item_code': item_code,
'today': today(),
'warehouses': from_warehouses
}, as_dict=1)
return batch_locations
def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty):
# gets all items available in different warehouses
filters = frappe._dict({
'item_code': item_code,
'actual_qty': ['>', 0]
})
if from_warehouses:
filters.warehouse = ['in', from_warehouses]
item_locations = frappe.get_all('Bin',
fields=['warehouse', 'actual_qty as qty'],
filters=filters,
limit=required_qty,
order_by='creation')
return item_locations
@frappe.whitelist()
def create_delivery_note(source_name, target_doc=None):
pick_list = frappe.get_doc('Pick List', source_name)
sales_orders = [d.sales_order for d in pick_list.locations]
sales_orders = set(sales_orders)
delivery_note = None
for sales_order in sales_orders:
delivery_note = create_delivery_note_from_sales_order(sales_order,
delivery_note, skip_item_mapping=True)
item_table_mapper = {
'doctype': 'Delivery Note Item',
'field_map': {
'rate': 'rate',
'name': 'so_detail',
'parent': 'against_sales_order',
},
'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
}
for location in pick_list.locations:
sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item)
dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper)
if dn_item:
dn_item.warehouse = location.warehouse
dn_item.qty = location.picked_qty
dn_item.batch_no = location.batch_no
dn_item.serial_no = location.serial_no
update_delivery_note_item(sales_order_item, dn_item, delivery_note)
set_delivery_note_missing_values(delivery_note)
delivery_note.pick_list = pick_list.name
return delivery_note
@frappe.whitelist()
def create_stock_entry(pick_list):
pick_list = frappe.get_doc(json.loads(pick_list))
if stock_entry_exists(pick_list.get('name')):
return frappe.msgprint(_('Stock Entry has been already created against this Pick List'))
stock_entry = frappe.new_doc('Stock Entry')
stock_entry.pick_list = pick_list.get('name')
stock_entry.purpose = pick_list.get('purpose')
stock_entry.set_stock_entry_type()
if pick_list.get('work_order'):
stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry)
elif pick_list.get('material_request'):
stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry)
else:
stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry)
stock_entry.set_incoming_rate()
stock_entry.set_actual_qty()
stock_entry.calculate_rate_and_amount(update_finished_item_rate=False)
return stock_entry.as_dict()
@frappe.whitelist()
def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict):
return frappe.db.sql("""
SELECT
`name`, `company`, `planned_start_date`
FROM
`tabWork Order`
WHERE
`status` not in ('Completed', 'Stopped')
AND `qty` > `material_transferred_for_manufacturing`
AND `docstatus` = 1
AND `company` = %(company)s
AND `name` like %(txt)s
ORDER BY
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name
LIMIT
%(start)s, %(page_length)s""",
{
'txt': "%%%s%%" % txt,
'_txt': txt.replace('%', ''),
'start': start,
'page_length': frappe.utils.cint(page_length),
'company': filters.get('company')
}, as_dict=as_dict)
@frappe.whitelist()
def target_document_exists(pick_list_name, purpose):
if purpose == 'Delivery against Sales Order':
return frappe.db.exists('Delivery Note', {
'pick_list': pick_list_name
})
return stock_entry_exists(pick_list_name)
@frappe.whitelist()
def get_item_details(item_code, uom=None):
details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1)
details.uom = uom or details.stock_uom
if uom:
details.update(get_conversion_factor(item_code, uom))
return details
def update_delivery_note_item(source, target, delivery_note):
cost_center = frappe.db.get_value('Project', delivery_note.project, 'cost_center')
if not cost_center:
cost_center = get_cost_center(source.item_code, 'Item', delivery_note.company)
if not cost_center:
cost_center = get_cost_center(source.item_group, 'Item Group', delivery_note.company)
target.cost_center = cost_center
def get_cost_center(for_item, from_doctype, company):
'''Returns Cost Center for Item or Item Group'''
return frappe.db.get_value('Item Default',
fieldname=['buying_cost_center'],
filters={
'parent': for_item,
'parenttype': from_doctype,
'company': company
})
def set_delivery_note_missing_values(target):
target.run_method('set_missing_values')
target.run_method('set_po_nos')
target.run_method('calculate_taxes_and_totals')
def stock_entry_exists(pick_list_name):
return frappe.db.exists('Stock Entry', {
'pick_list': pick_list_name
})
def update_stock_entry_based_on_work_order(pick_list, stock_entry):
work_order = frappe.get_doc("Work Order", pick_list.get('work_order'))
stock_entry.work_order = work_order.name
stock_entry.company = work_order.company
stock_entry.from_bom = 1
stock_entry.bom_no = work_order.bom_no
stock_entry.use_multi_level_bom = work_order.use_multi_level_bom
stock_entry.fg_completed_qty = pick_list.for_qty
if work_order.bom_no:
stock_entry.inspection_required = frappe.db.get_value('BOM',
work_order.bom_no, 'inspection_required')
is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group')
if not (is_wip_warehouse_group and work_order.skip_transfer):
wip_warehouse = work_order.wip_warehouse
else:
wip_warehouse = None
stock_entry.to_warehouse = wip_warehouse
stock_entry.project = work_order.project
for location in pick_list.locations:
item = frappe._dict()
update_common_item_properties(item, location)
item.t_warehouse = wip_warehouse
stock_entry.append('items', item)
return stock_entry
def update_stock_entry_based_on_material_request(pick_list, stock_entry):
for location in pick_list.locations:
target_warehouse = None
if location.material_request_item:
target_warehouse = frappe.get_value('Material Request Item',
location.material_request_item, 'warehouse')
item = frappe._dict()
update_common_item_properties(item, location)
item.t_warehouse = target_warehouse
stock_entry.append('items', item)
return stock_entry
def update_stock_entry_items_with_no_reference(pick_list, stock_entry):
for location in pick_list.locations:
item = frappe._dict()
update_common_item_properties(item, location)
stock_entry.append('items', item)
return stock_entry
def update_common_item_properties(item, location):
item.item_code = location.item_code
item.s_warehouse = location.warehouse
item.qty = location.picked_qty * location.conversion_factor
item.transfer_qty = location.picked_qty
item.uom = location.uom
item.conversion_factor = location.conversion_factor
item.stock_uom = location.stock_uom
item.material_request = location.material_request
item.serial_no = location.serial_no
item.batch_no = location.batch_no
item.material_request_item = location.material_request_item

View File

@@ -0,0 +1,12 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'pick_list',
'transactions': [
{
'items': ['Stock Entry', 'Delivery Note']
},
]
}

View File

@@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \
import EmptyStockReconciliationItemsError
class TestPickList(unittest.TestCase):
def test_pick_list_picks_warehouse_for_each_item(self):
try:
frappe.get_doc({
'doctype': 'Stock Reconciliation',
'company': '_Test Company',
'purpose': 'Opening Stock',
'expense_account': 'Temporary Opening - _TC',
'items': [{
'item_code': '_Test Item Home Desktop 100',
'warehouse': '_Test Warehouse - _TC',
'valuation_rate': 100,
'qty': 5
}]
}).submit()
except EmptyStockReconciliationItemsError:
pass
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'customer': '_Test Customer',
'items_based_on': 'Sales Order',
'locations': [{
'item_code': '_Test Item Home Desktop 100',
'qty': 5,
'stock_qty': 5,
'conversion_factor': 1,
'sales_order': '_T-Sales Order-1',
'sales_order_item': '_T-Sales Order-1_item',
}]
})
pick_list.set_item_locations()
self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100')
self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
self.assertEqual(pick_list.locations[0].qty, 5)
def test_pick_list_splits_row_according_to_warhouse_availability(self):
try:
frappe.get_doc({
'doctype': 'Stock Reconciliation',
'company': '_Test Company',
'purpose': 'Opening Stock',
'expense_account': 'Temporary Opening - _TC',
'items': [{
'item_code': '_Test Item Warehouse Group Wise Reorder',
'warehouse': '_Test Warehouse Group-C1 - _TC',
'valuation_rate': 100,
'qty': 5
}]
}).submit()
except EmptyStockReconciliationItemsError:
pass
try:
frappe.get_doc({
'doctype': 'Stock Reconciliation',
'company': '_Test Company',
'purpose': 'Opening Stock',
'expense_account': 'Temporary Opening - _TC',
'items': [{
'item_code': '_Test Item Warehouse Group Wise Reorder',
'warehouse': '_Test Warehouse 2 - _TC',
'valuation_rate': 400,
'qty': 10
}]
}).submit()
except EmptyStockReconciliationItemsError:
pass
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'customer': '_Test Customer',
'items_based_on': 'Sales Order',
'locations': [{
'item_code': '_Test Item Warehouse Group Wise Reorder',
'qty': 1000,
'stock_qty': 1000,
'conversion_factor': 1,
'sales_order': '_T-Sales Order-1',
'sales_order_item': '_T-Sales Order-1_item',
}]
})
pick_list.set_item_locations()
self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder')
self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC')
self.assertEqual(pick_list.locations[0].qty, 5)
self.assertEqual(pick_list.locations[1].item_code, '_Test Item Warehouse Group Wise Reorder')
self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse 2 - _TC')
self.assertEqual(pick_list.locations[1].qty, 10)
def test_pick_list_shows_serial_no_for_serialized_item(self):
stock_reconciliation = frappe.get_doc({
'doctype': 'Stock Reconciliation',
'company': '_Test Company',
'items': [{
'item_code': '_Test Serialized Item',
'warehouse': '_Test Warehouse - _TC',
'valuation_rate': 100,
'qty': 5,
'serial_no': '123450\n123451\n123452\n123453\n123454'
}]
})
stock_reconciliation.submit()
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'customer': '_Test Customer',
'items_based_on': 'Sales Order',
'locations': [{
'item_code': '_Test Serialized Item',
'qty': 1000,
'stock_qty': 1000,
'conversion_factor': 1,
'sales_order': '_T-Sales Order-1',
'sales_order_item': '_T-Sales Order-1_item',
}]
})
pick_list.set_item_locations()
self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item')
self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
self.assertEqual(pick_list.locations[0].qty, 5)
self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454')
def test_pick_list_for_items_from_multiple_sales_orders(self):
try:
frappe.get_doc({
'doctype': 'Stock Reconciliation',
'company': '_Test Company',
'purpose': 'Opening Stock',
'expense_account': 'Temporary Opening - _TC',
'items': [{
'item_code': '_Test Item Home Desktop 100',
'warehouse': '_Test Warehouse - _TC',
'valuation_rate': 100,
'qty': 10
}]
}).submit()
except EmptyStockReconciliationItemsError:
pass
sales_order = frappe.get_doc({
'doctype': "Sales Order",
'customer': '_Test Customer',
'company': '_Test Company',
'items': [{
'item_code': '_Test Item Home Desktop 100',
'qty': 10,
'delivery_date': frappe.utils.today()
}],
})
sales_order.submit()
pick_list = frappe.get_doc({
'doctype': 'Pick List',
'company': '_Test Company',
'customer': '_Test Customer',
'items_based_on': 'Sales Order',
'locations': [{
'item_code': '_Test Item Home Desktop 100',
'qty': 5,
'stock_qty': 5,
'conversion_factor': 1,
'sales_order': '_T-Sales Order-1',
'sales_order_item': '_T-Sales Order-1_item',
}, {
'item_code': '_Test Item Home Desktop 100',
'qty': 5,
'stock_qty': 5,
'conversion_factor': 1,
'sales_order': sales_order.name,
'sales_order_item': sales_order.items[0].name,
}]
})
pick_list.set_item_locations()
self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100')
self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC')
self.assertEqual(pick_list.locations[0].qty, 5)
self.assertEqual(pick_list.locations[0].sales_order_item, '_T-Sales Order-1_item')
self.assertEqual(pick_list.locations[1].item_code, '_Test Item Home Desktop 100')
self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse - _TC')
self.assertEqual(pick_list.locations[1].qty, 5)
self.assertEqual(pick_list.locations[1].sales_order_item, sales_order.items[0].name)
# def test_pick_list_skips_items_in_expired_batch(self):
# pass
# def test_pick_list_from_sales_order(self):
# pass
# def test_pick_list_from_work_order(self):
# pass
# def test_pick_list_from_material_request(self):
# pass

View File

@@ -0,0 +1,182 @@
{
"creation": "2019-07-11 16:01:22.832885",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"item_code",
"item_name",
"column_break_2",
"description",
"section_break_5",
"warehouse",
"quantity_section",
"qty",
"stock_qty",
"picked_qty",
"column_break_11",
"uom",
"conversion_factor",
"stock_uom",
"serial_no_and_batch_section",
"serial_no",
"column_break_20",
"batch_no",
"column_break_15",
"sales_order",
"sales_order_item",
"material_request",
"material_request_item"
],
"fields": [
{
"default": "1",
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Qty"
},
{
"fieldname": "picked_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Picked Qty"
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Warehouse",
"options": "Warehouse",
"read_only": 1
},
{
"fetch_from": "item_code.item_name",
"fieldname": "item_name",
"fieldtype": "Data",
"label": "Item Name",
"read_only": 1
},
{
"fetch_from": "item_code.description",
"fieldname": "description",
"fieldtype": "Text",
"label": "Description",
"read_only": 1
},
{
"depends_on": "serial_no",
"fieldname": "serial_no",
"fieldtype": "Small Text",
"label": "Serial No"
},
{
"depends_on": "batch_no",
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_5",
"fieldtype": "Section Break"
},
{
"fieldname": "stock_uom",
"fieldtype": "Link",
"label": "Stock UOM",
"options": "UOM",
"read_only": 1
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"fieldname": "uom",
"fieldtype": "Link",
"label": "UOM",
"options": "UOM"
},
{
"fieldname": "conversion_factor",
"fieldtype": "Float",
"label": "UOM Conversion Factor",
"read_only": 1
},
{
"fieldname": "stock_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Stock Qty",
"read_only": 1
},
{
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item",
"options": "Item"
},
{
"fieldname": "quantity_section",
"fieldtype": "Section Break",
"label": "Quantity"
},
{
"fieldname": "column_break_15",
"fieldtype": "Section Break",
"label": "Reference"
},
{
"fieldname": "sales_order",
"fieldtype": "Link",
"label": "Sales Order",
"options": "Sales Order",
"read_only": 1
},
{
"fieldname": "sales_order_item",
"fieldtype": "Data",
"label": "Sales Order Item",
"read_only": 1
},
{
"fieldname": "serial_no_and_batch_section",
"fieldtype": "Section Break",
"label": "Serial No and Batch"
},
{
"fieldname": "column_break_20",
"fieldtype": "Column Break"
},
{
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
"options": "Material Request",
"read_only": 1
},
{
"fieldname": "material_request_item",
"fieldtype": "Data",
"label": "Material Request Item",
"read_only": 1
}
],
"istable": 1,
"modified": "2019-08-29 21:28:39.539007",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class PickListItem(Document):
pass

View File

@@ -17,6 +17,7 @@
"purchase_order", "purchase_order",
"delivery_note_no", "delivery_note_no",
"sales_invoice_no", "sales_invoice_no",
"pick_list",
"purchase_receipt_no", "purchase_receipt_no",
"col2", "col2",
"posting_date", "posting_date",
@@ -613,12 +614,19 @@
{ {
"fieldname": "dimension_col_break", "fieldname": "dimension_col_break",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"fieldname": "pick_list",
"fieldtype": "Link",
"label": "Pick List",
"options": "Pick List",
"read_only": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-07-14 17:41:39.257508", "modified": "2019-08-22 17:11:42.074154",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry", "name": "Stock Entry",

View File

@@ -525,15 +525,21 @@ class StockEntry(StockController):
backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings", backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings",
"backflush_raw_materials_of_subcontract_based_on") "backflush_raw_materials_of_subcontract_based_on")
qty_allowance = flt(frappe.db.get_single_value("Buying Settings",
"over_transfer_allowance"))
if (self.purpose == "Send to Subcontractor" and self.purchase_order and if (self.purpose == "Send to Subcontractor" and self.purchase_order and
backflush_raw_materials_based_on == 'BOM'): backflush_raw_materials_based_on == 'BOM'):
purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
for se_item in self.items: for se_item in self.items:
item_code = se_item.original_item or se_item.item_code item_code = se_item.original_item or se_item.item_code
precision = cint(frappe.db.get_default("float_precision")) or 3 precision = cint(frappe.db.get_default("float_precision")) or 3
total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
if d.rm_item_code == item_code]) if d.rm_item_code == item_code])
if not total_allowed:
total_allowed = required_qty + (required_qty * (qty_allowance/100))
if not required_qty:
frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}")
.format(se_item.item_code, self.purchase_order)) .format(se_item.item_code, self.purchase_order))
total_supplied = frappe.db.sql("""select sum(transfer_qty) total_supplied = frappe.db.sql("""select sum(transfer_qty)
@@ -1110,6 +1116,7 @@ class StockEntry(StockController):
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
se_child.subcontracted_item = item_dict[d].get("main_item_code") se_child.subcontracted_item = item_dict[d].get("main_item_code")
se_child.original_item = item_dict[d].get("original_item") se_child.original_item = item_dict[d].get("original_item")
se_child.po_detail = item_dict[d].get("po_detail")
if item_dict[d].get("idx"): if item_dict[d].get("idx"):
se_child.idx = item_dict[d].get("idx") se_child.idx = item_dict[d].get("idx")
@@ -1161,7 +1168,14 @@ class StockEntry(StockController):
where po.name = poitemsup.parent where po.name = poitemsup.parent
and po.name = %s""", self.purchase_order)) and po.name = %s""", self.purchase_order))
#Update reserved sub contracted quantity in bin based on Supplied Item Details #Update Supplied Qty in PO Supplied Items
frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos
SET pos.supplied_qty = (SELECT ifnull(sum(transfer_qty), 0) FROM `tabStock Entry Detail` sed
WHERE pos.name = sed.po_detail and sed.docstatus = 1)
WHERE pos.docstatus = 1""")
#Update reserved sub contracted quantity in bin based on Supplied Item Details and
for d in self.get("items"): for d in self.get("items"):
item_code = d.get('original_item') or d.get('item_code') item_code = d.get('original_item') or d.get('item_code')
reserve_warehouse = item_wh.get(item_code) reserve_warehouse = item_wh.get(item_code)

View File

@@ -59,6 +59,7 @@
"reference_section", "reference_section",
"against_stock_entry", "against_stock_entry",
"ste_detail", "ste_detail",
"po_detail",
"column_break_51", "column_break_51",
"transferred_qty", "transferred_qty",
"reference_purchase_receipt", "reference_purchase_receipt",
@@ -480,11 +481,20 @@
"label": "Project", "label": "Project",
"options": "Project", "options": "Project",
"read_only": 1 "read_only": 1
},
{
"fieldname": "po_detail",
"fieldtype": "Data",
"hidden": 1,
"label": "PO Supplied Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2019-07-12 11:34:53.190749", "modified": "2019-08-20 14:01:02.319754",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry Detail", "name": "Stock Entry Detail",

View File

@@ -22,7 +22,7 @@ sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
@frappe.whitelist() @frappe.whitelist()
def get_item_details(args, doc=None): def get_item_details(args, doc=None, overwrite_warehouse=True):
""" """
args = { args = {
"item_code": "", "item_code": "",
@@ -44,11 +44,12 @@ def get_item_details(args, doc=None):
"set_warehouse": "" "set_warehouse": ""
} }
""" """
args = process_args(args) args = process_args(args)
item = frappe.get_cached_doc("Item", args.item_code) item = frappe.get_cached_doc("Item", args.item_code)
validate_item_details(args, item) validate_item_details(args, item)
out = get_basic_details(args, item) out = get_basic_details(args, item, overwrite_warehouse)
get_item_tax_template(args, item, out) get_item_tax_template(args, item, out)
out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \ out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \
@@ -178,7 +179,7 @@ def validate_item_details(args, item):
throw(_("Item {0} must be a Sub-contracted Item").format(item.name)) throw(_("Item {0} must be a Sub-contracted Item").format(item.name))
def get_basic_details(args, item): def get_basic_details(args, item, overwrite_warehouse=True):
""" """
:param args: { :param args: {
"item_code": "", "item_code": "",
@@ -225,14 +226,26 @@ def get_basic_details(args, item):
item_group_defaults = get_item_group_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, args.company)
brand_defaults = get_brand_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, args.company)
warehouse = (args.get("set_warehouse") or item_defaults.get("default_warehouse") or if overwrite_warehouse or not args.warehouse:
item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse) warehouse = (
args.get("set_warehouse") or
item_defaults.get("default_warehouse") or
item_group_defaults.get("default_warehouse") or
brand_defaults.get("default_warehouse") or
args.warehouse
)
if not warehouse: if not warehouse:
defaults = frappe.defaults.get_defaults() or {} defaults = frappe.defaults.get_defaults() or {}
if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", warehouse_exists = frappe.db.exists("Warehouse", {
{'name': defaults.default_warehouse, 'company': args.company}): 'name': defaults.default_warehouse,
warehouse = defaults.default_warehouse 'company': args.company
})
if defaults.get("default_warehouse") and warehouse_exists:
warehouse = defaults.default_warehouse
else:
warehouse = args.warehouse
if args.get('doctype') == "Material Request" and not args.get('material_request_type'): if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
args['material_request_type'] = frappe.db.get_value('Material Request', args['material_request_type'] = frappe.db.get_value('Material Request',

View File

View File

@@ -0,0 +1,23 @@
{
"align_labels_right": 1,
"creation": "2019-08-02 07:27:42.533305",
"custom_format": 0,
"disabled": 0,
"doc_type": "Pick List",
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\t\\t\\t\\t<h2>Pick List<br><small>{{ doc.name }}</small>\\t\\t\\t\\t</h2></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"customer\", \"label\": \"Customer\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"purpose\", \"label\": \"Purpose\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"stock_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"locations\", \"label\": \"Item Locations\"}]",
"idx": 0,
"line_breaks": 1,
"modified": "2019-08-30 15:58:27.807219",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
"owner": "Administrator",
"print_format_builder": 1,
"print_format_type": "Jinja",
"raw_printing": 0,
"show_section_headings": 1,
"standard": "Yes"
}

View File

@@ -227,9 +227,9 @@ class update_entries_after(object):
elif actual_qty < 0: elif actual_qty < 0:
# In case of delivery/stock issue, get average purchase rate # In case of delivery/stock issue, get average purchase rate
# of serial nos of current entry # of serial nos of current entry
stock_value_change = -1 * flt(frappe.db.sql("""select sum(purchase_rate) stock_value_change = -1 * flt(frappe.get_all("Serial No",
from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))), fields=["sum(purchase_rate)"],
tuple(serial_no))[0][0]) filters = {'name': ('in', serial_no)}, as_list=1)[0][0])
new_stock_qty = self.qty_after_transaction + actual_qty new_stock_qty = self.qty_after_transaction + actual_qty