Merge branch 'develop' of https://github.com/frappe/erpnext into feat-customer-credit-limit

This commit is contained in:
Mangesh-Khairnar 2019-09-02 23:18:56 +05:30
commit 4bcc221ee3
61 changed files with 3038 additions and 5293 deletions

View File

@ -239,8 +239,6 @@ class TestBudget(unittest.TestCase):
budget.cancel()
jv.cancel()
frappe.delete_doc('Journal Entry', jv.name)
frappe.delete_doc('Cost Center', cost_center)
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "Project":

View File

@ -49,7 +49,6 @@ class TestLoyaltyProgram(unittest.TestCase):
# cancel and delete
for d in [si_redeem, si_original]:
d.cancel()
frappe.delete_doc('Sales Invoice', d.name)
def test_loyalty_points_earned_multiple_tier(self):
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
@ -91,7 +90,6 @@ class TestLoyaltyProgram(unittest.TestCase):
# cancel and delete
for d in [si_redeem, si_original]:
d.cancel()
frappe.delete_doc('Sales Invoice', d.name)
def test_cancel_sales_invoice(self):
''' cancelling the sales invoice should cancel the earned points'''
@ -143,7 +141,6 @@ class TestLoyaltyProgram(unittest.TestCase):
d.cancel()
except frappe.TimestampMismatchError:
frappe.get_doc('Sales Invoice', d.name).cancel()
frappe.delete_doc('Sales Invoice', d.name)
def test_loyalty_points_for_dashboard(self):
doc = frappe.get_doc('Customer', 'Test Loyalty Customer')

View File

@ -947,10 +947,15 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
paid_amount = abs(outstanding_amount)
if bank_amount:
received_amount = bank_amount
else:
received_amount = paid_amount * doc.conversion_rate
else:
received_amount = abs(outstanding_amount)
if bank_amount:
paid_amount = bank_amount
else:
# if party account currency and bank currency is different then populate paid amount as well
paid_amount = received_amount * doc.conversion_rate
pe = frappe.new_doc("Payment Entry")
pe.payment_type = payment_type

View File

@ -227,7 +227,7 @@ def get_contacts(customers):
customers = [frappe._dict({'name': customers})]
for data in customers:
contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact`
contact = frappe.db.sql(""" select email_id, phone from `tabContact`
where is_primary_contact=1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Contact')""", data.name, as_dict=1)

View File

@ -818,7 +818,6 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(expected_gl_entries[i][2], gle.credit)
si.cancel()
frappe.delete_doc('Sales Invoice', si.name)
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)

View File

@ -48,7 +48,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
#delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def test_single_threshold_tds(self):
invoices = []
@ -83,7 +82,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def test_single_threshold_tds_with_previous_vouchers(self):
invoices = []
@ -102,7 +100,6 @@ class TestTaxWithholdingCategory(unittest.TestCase):
# delete invoices to avoid clashing
for d in invoices:
d.cancel()
frappe.delete_doc("Purchase Invoice", d.name)
def create_purchase_invoice(**args):
# return sales invoice doc object

View File

@ -816,7 +816,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
contact = me.contacts[data.name];
if(reg.test(data.name.toLowerCase())
|| reg.test(data.customer_name.toLowerCase())
|| (contact && reg.test(contact["mobile_no"]))
|| (contact && reg.test(contact["phone"]))
|| (data.customer_group && reg.test(data.customer_group.toLowerCase()))){
return data;
@ -834,7 +833,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if(contact && !c['phone']) {
c["phone"] = contact["phone"];
c["email_id"] = contact["email_id"];
c["mobile_no"] = contact["mobile_no"];
}
me.customers_mapper.push({
@ -844,10 +842,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
customer_group: c.customer_group,
territory: c.territory,
phone: contact ? contact["phone"] : '',
mobile_no: contact ? contact["mobile_no"] : '',
email_id: contact ? contact["email_id"] : '',
searchtext: ['customer_name', 'customer_group', 'name', 'value',
'label', 'email_id', 'phone', 'mobile_no']
'label', 'email_id', 'phone']
.map(key => c[key]).join(' ')
.toLowerCase()
});

View File

@ -456,8 +456,6 @@ class TestAsset(unittest.TestCase):
self.assertEqual(gle, expected_gle)
si.cancel()
frappe.delete_doc("Sales Invoice", si.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
def test_asset_expected_value_after_useful_life(self):

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,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-06-25 11:04:03",
"custom": 0,
"description": "Settings for Buying Module",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 0,
"field_order": [
"supp_master_name",
"supplier_group",
"buying_price_list",
"column_break_3",
"po_required",
"pr_required",
"maintain_same_rate",
"allow_multiple_items",
"subcontract",
"backflush_raw_materials_of_subcontract_based_on",
"column_break_11",
"over_transfer_allowance"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
"options": "Supplier Name\nNaming Series"
},
{
"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
"options": "Supplier Group"
},
{
"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
"options": "Price List"
},
{
"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
"fieldtype": "Column Break"
},
{
"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
"options": "No\nYes"
},
{
"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
"options": "No\nYes"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "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
"label": "Maintain same rate throughout purchase cycle"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "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
"label": "Allow Item to be added multiple times in a transaction"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"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
"label": "Subcontract"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Material Transferred for Subcontract",
"fieldname": "backflush_raw_materials_of_subcontract_based_on",
"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": "Backflush Raw Materials of Subcontract Based On",
"length": 0,
"no_copy": 0,
"options": "BOM\nMaterial Transferred for Subcontract",
"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
"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,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-31 07:52:38.062488",
"modified": "2019-08-20 13:13:09.055189",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 0,
"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
}
],
"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"]
items_dict = {
rm_item_code: {
"po_detail": rm_item_data.get("name"),
"item_name": rm_item_data["item_name"],
"description": item_wh.get(rm_item_code, {}).get('description', ""),
'qty': rm_item_data["qty"],

View File

@ -1,404 +1,134 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:27:42",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"editable_grid": 1,
"field_order": [
"main_item_code",
"rm_item_code",
"required_qty",
"supplied_qty",
"rate",
"amount",
"column_break_6",
"bom_detail_no",
"reference_name",
"conversion_factor",
"stock_uom",
"reserve_warehouse"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "main_item_code",
"fieldtype": "Link",
"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": "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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "rm_item_code",
"fieldtype": "Link",
"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": "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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "required_qty",
"fieldtype": "Float",
"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,
"label": "Required Qty",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "rate",
"fieldtype": "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
"options": "Company:company:default_currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amount",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "conversion_factor",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_uom",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "reserve_warehouse",
"fieldtype": "Link",
"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": "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
"options": "Warehouse"
},
{
"fieldname": "supplied_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Supplied Qty",
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 1,
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-01-07 16:51:58.016007",
"modified": "2019-08-20 13:37:32.702068",
"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
"permissions": []
}

View File

@ -73,6 +73,10 @@ def set_caller_information(doc, state):
# contact_name or lead_name
display_name_field = '{}_name'.format(fieldname)
# Contact now has all the nos saved in child table
if doc.doctype == 'Contact':
numbers = [d.phone for d in doc.phone_nos]
for number in numbers:
number = strip_number(number)
if not number: continue

View File

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

View File

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

View File

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

View File

@ -263,7 +263,7 @@ class AccountsController(TransactionBase):
if self.get("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():
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":
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]
elif self.doctype == "Sales Order":
e = [d.item_code, d.description, d.warehouse, d.batch_no or '']
elif self.doctype in ["Sales Order", "Quotation"]:
e = [d.item_code, d.description, d.warehouse, '']
f = [d.item_code, d.description]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:

View File

@ -45,15 +45,16 @@ class TestOpportunity(unittest.TestCase):
# create new customer and create new contact against 'new.opportunity@example.com'
customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True)
frappe.get_doc({
contact = frappe.get_doc({
"doctype": "Contact",
"email_id": new_lead_email_id,
"first_name": "_Test Opportunity Customer",
"links": [{
"link_doctype": "Customer",
"link_name": customer.name
}]
}).insert(ignore_permissions=True)
})
contact.add_email(new_lead_email_id)
contact.insert(ignore_permissions=True)
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
self.assertTrue(opp_doc.party_name)

View File

@ -3,7 +3,6 @@
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
import requests
import frappe

View File

@ -58,7 +58,7 @@ class ShopifySettings(Document):
d.raise_for_status()
self.update_webhook_table(method, d.json())
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):
session = get_request_session()
@ -71,7 +71,7 @@ class ShopifySettings(Document):
res.raise_for_status()
deleted_webhooks.append(d)
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:
self.remove(d)

View File

@ -45,7 +45,6 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
expense_claim2.cancel()
frappe.delete_doc("Expense Claim", expense_claim2.name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)

View File

@ -95,7 +95,6 @@ def process_expired_allocation():
'expire_carry_forwarded_leaves_after_days': (">", 0)
}, fieldname=['name'])
if leave_type_records:
leave_type = [record[0] for record in leave_type_records]
expired_allocation = frappe.db.sql_list("""SELECT name

View File

@ -30,7 +30,7 @@ class LoanApplication(Document):
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
min_repayment_amount = self.loan_amount*monthly_interest_rate
if self.repayment_amount - min_repayment_amount <= 0:
if (self.repayment_amount - min_repayment_amount) <= 0:
frappe.throw(_("Repayment Amount must be greater than " \
+ str(flt(min_repayment_amount, 2))))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
@ -58,6 +58,9 @@ def make_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Loan Application", source_name, {
"Loan Application": {
"doctype": "Loan",
"field_map": {
"repayment_amount": "monthly_repayment_amount"
},
"validation": {
"docstatus": ["=", 1]
}

View File

@ -68,12 +68,13 @@ def make_contact(supplier):
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': supplier.supplier_name,
'email_id': supplier.supplier_email,
'is_primary_contact': 1,
'links': [
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
]
}).insert()
})
contact.add_email(supplier.supplier_email)
contact.insert()
else:
contact = frappe.get_doc('Contact', contact_name)

View File

@ -4,16 +4,17 @@
frappe.ui.form.on("Work Order", {
setup: function(frm) {
frm.custom_make_buttons = {
'Stock Entry': 'Make Stock Entry',
}
'Stock Entry': 'Start',
'Pick List': 'Create Pick List',
};
// Set query for warehouses
frm.set_query("wip_warehouse", function(doc) {
frm.set_query("wip_warehouse", function() {
return {
filters: {
'company': frm.doc.company,
}
}
};
});
frm.set_query("source_warehouse", function() {
@ -21,7 +22,7 @@ frappe.ui.form.on("Work Order", {
filters: {
'company': frm.doc.company,
}
}
};
});
frm.set_query("source_warehouse", "required_items", function() {
@ -29,7 +30,7 @@ frappe.ui.form.on("Work Order", {
filters: {
'company': frm.doc.company,
}
}
};
});
frm.set_query("sales_order", function() {
@ -37,7 +38,7 @@ frappe.ui.form.on("Work Order", {
filters: {
"status": ["not in", ["Closed", "On Hold"]]
}
}
};
});
frm.set_query("fg_warehouse", function() {
@ -46,7 +47,7 @@ frappe.ui.form.on("Work Order", {
'company': frm.doc.company,
'is_group': 0
}
}
};
});
frm.set_query("scrap_warehouse", function() {
@ -55,7 +56,7 @@ frappe.ui.form.on("Work Order", {
'company': frm.doc.company,
'is_group': 0
}
}
};
});
// Set query for BOM
@ -64,8 +65,10 @@ frappe.ui.form.on("Work Order", {
return {
query: "erpnext.controllers.queries.bom",
filters: {item: cstr(frm.doc.production_item)}
};
} else {
frappe.msgprint(__("Please enter Production Item first"));
}
} else msgprint(__("Please enter Production Item first"));
});
// Set query for FG Item
@ -76,7 +79,7 @@ frappe.ui.form.on("Work Order", {
['is_stock_item', '=',1],
['default_bom', '!=', '']
]
}
};
});
// Set query for FG Item
@ -85,12 +88,12 @@ frappe.ui.form.on("Work Order", {
filters:[
['Project', 'status', 'not in', 'Completed, Cancelled']
]
}
};
});
// formatter for work order 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) {
@ -133,7 +136,7 @@ frappe.ui.form.on("Work Order", {
if(not_completed && not_completed.length) {
frm.add_custom_button(__('Create Job Card'), () => {
frm.trigger("make_job_card")
frm.trigger("make_job_card");
}).addClass('btn-primary');
}
}
@ -151,7 +154,7 @@ frappe.ui.form.on("Work Order", {
condition: (d) => {
if (d.allow_alternative_item) {return true;}
}
})
});
});
}
}
@ -285,13 +288,13 @@ frappe.ui.form.on("Work Order", {
if(!frm.doc.skip_transfer){
var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty;
if(pending_complete) {
var title = __('{0} items in progress', [pending_complete]);
var width = ((pending_complete / frm.doc.qty * 100) - added_min);
title = __('{0} items in progress', [pending_complete]);
bars.push({
'title': title,
'width': (width > 100 ? "99.5" : width) + '%',
'progress_class': 'progress-bar-warning'
})
});
message = message + '. ' + title;
}
}
@ -377,7 +380,7 @@ frappe.ui.form.on("Work Order", {
filters: [
["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,
"available_qty_at_source_warehouse", r.message);
}
})
});
}
}
})
});
frappe.ui.form.on("Work Order Operation", {
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_total_cost(frm);
}
})
});
}
},
time_in_mins: function(frm, cdt, cdn) {
@ -451,6 +454,9 @@ erpnext.work_order = {
if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty))
&& frm.doc.status != 'Stopped') {
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() {
erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture');
});
@ -519,8 +525,8 @@ erpnext.work_order = {
calculate_total_cost: function(frm) {
var variable_cost = frm.doc.actual_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))
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));
},
set_default_warehouse: function(frm) {
@ -530,43 +536,70 @@ erpnext.work_order = {
callback: function(r) {
if (!r.exe) {
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);
}
}
});
}
},
get_max_transferable_qty: (frm, purpose) => {
let max = 0;
if (frm.doc.skip_transfer) return max;
if (purpose === 'Manufacture') {
max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty);
} else {
max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
}
return flt(max, precision('qty'));
},
show_prompt_for_qty_input: function(frm, purpose) {
let max = this.get_max_transferable_qty(frm, purpose);
return new Promise((resolve, reject) => {
frappe.prompt({
fieldtype: 'Float',
label: __('Qty for {0}', [purpose]),
fieldname: 'qty',
description: __('Max: {0}', [max]),
default: max
}, data => {
if (data.qty > max) {
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
reject();
}
data.purpose = purpose;
resolve(data);
}, __('Select Quantity'), __('Create'));
});
},
make_se: function(frm, purpose) {
if(!frm.doc.skip_transfer){
var max = (purpose === "Manufacture") ?
flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) :
flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing);
} else {
var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
}
max = flt(max, precision("qty"));
frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty",
description: __("Max: {0}", [max]), 'default': max }, function(data)
{
if(data.qty > max) {
frappe.msgprint(__("Quantity must not be more than {0}", [max]));
return;
}
frappe.call({
method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry",
args: {
"work_order_id": frm.doc.name,
"purpose": purpose,
"qty": data.qty
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
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) {
@ -606,6 +639,6 @@ erpnext.work_order = {
frm.reload_doc();
}
}
})
}
});
}
};

View File

@ -72,6 +72,7 @@
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"options": "MFG-WO-.YYYY.-",
"print_hide": 1,
"reqd": 1,
@ -467,7 +468,7 @@
"idx": 1,
"image_field": "image",
"is_submittable": 1,
"modified": "2019-07-31 00:13:38.218277",
"modified": "2019-08-28 12:29:35.315239",
"modified_by": "Administrator",
"module": "Manufacturing",
"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 erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
from erpnext.utilities.transaction_base import validate_uom_is_integer
from frappe.model.mapper import get_mapped_doc
class OverProductionError(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:
if d.operation == operation and d.workstation == workstation:
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',
'transactions': [
{
'items': ['Stock Entry', 'Job Card']
'items': ['Pick List', 'Stock Entry', 'Job Card']
}
]
}

View File

@ -96,7 +96,7 @@ class Timesheet(Document):
for time in self.time_logs:
if time.from_time and time.to_time:
if flt(std_working_hours) > 0:
if flt(std_working_hours) and date_diff(time.to_time, time.from_time):
time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time)
else:
if not time.hours:

View File

@ -14,20 +14,33 @@
style="margin-top:-3px; margin-right: -5px;">
{%= __("Edit") %}</a>
</p>
{% if (contact_list[i].phone || contact_list[i].mobile_no ||
contact_list[i].email_id) { %}
{% if (contact_list[i].phones || contact_list[i].email_ids) { %}
<p>
{% if(contact_list[i].phone) { %}
{%= __("Phone") %}: {%= contact_list[i].phone %}<br>
{% } %}
{% if(contact_list[i].mobile_no) { %}
{%= __("Mobile No.") %}: {%= contact_list[i].mobile_no %}<br>
{%= __("Phone") %}: {%= contact_list[i].phone %}<span class="text-muted"> ({%= __("Primary") %})</span><br>
{% endif %}
{% if(contact_list[i].phone_nos) { %}
{% for(var j=0, k=contact_list[i].phone_nos.length; j<k; j++) { %}
{%= __("Phone") %}: {%= contact_list[i].phone_nos[j].phone %}<br>
{% } %}
{% endif %}
</p>
<p>
{% if(contact_list[i].email_id) { %}
{%= __("Email Address") %}: {%= contact_list[i].email_id %}
{%= __("Email") %}: {%= contact_list[i].email_id %}<span class="text-muted"> ({%= __("Primary") %})</span><br>
{% endif %}
{% if(contact_list[i].email_ids) { %}
{% for(var j=0, k=contact_list[i].email_ids.length; j<k; j++) { %}
{%= __("Email") %}: {%= contact_list[i].email_ids[j].email_id %}<br>
{% } %}
{% endif %}
</p>
{% endif %}
<p>
{% if (contact_list[i].address) { %}
{%= __("Address") %}: {%= contact_list[i].address %}<br>
{% endif %}
</p>
</div>
{% } %}
{% if(!contact_list.length) { %}

View File

@ -342,14 +342,15 @@ def make_contact(args, is_primary_contact=1):
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': args.get('name'),
'mobile_no': args.get('mobile_no'),
'email_id': args.get('email_id'),
'is_primary_contact': is_primary_contact,
'links': [{
'link_doctype': args.get('doctype'),
'link_name': args.get('name')
}]
}).insert()
})
contact.add_email(args.get('email_id'))
contact.add_phone(args.get('mobile_no'))
contact.insert()
return contact

View File

@ -7,6 +7,7 @@ frappe.ui.form.on("Sales Order", {
setup: function(frm) {
frm.custom_make_buttons = {
'Delivery Note': 'Delivery',
'Pick List': 'Pick List',
'Sales Invoice': 'Invoice',
'Material Request': 'Material Request',
'Purchase Order': 'Purchase Order',
@ -110,6 +111,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
let allow_delivery = false;
if (doc.docstatus==1) {
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
if(this.frm.has_perm("submit")) {
if(doc.status === 'On Hold') {
// un-hold
@ -233,6 +236,13 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
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() {
var me = this;
this.frm.call({

View File

@ -559,7 +559,7 @@ def make_project(source_name, target_doc=None):
return doc
@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):
target.ignore_pricing_rule = 1
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_group.get("buying_cost_center")
target_doc = get_mapped_doc("Sales Order", source_name, {
mapper = {
"Sales Order": {
"doctype": "Delivery Note",
"validation": {
"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": {
"doctype": "Sales Taxes and Charges",
"add_if_empty": True
@ -609,7 +599,21 @@ def make_delivery_note(source_name, target_doc=None):
"doctype": "Sales Team",
"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
@ -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):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
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': [
{
'label': _('Fulfillment'),
'items': ['Sales Invoice', 'Delivery Note']
'items': ['Sales Invoice', 'Pick List', 'Delivery Note']
},
{
'label': _('Purchasing'),

View File

@ -31,7 +31,7 @@ class SMSCenter(Document):
self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and
c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and
c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
elif self.send_to == 'All Lead (Open)':

View File

@ -26,6 +26,10 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate(self):
super(ItemGroup, self).validate()
if not self.parent_item_group and not frappe.flags.in_test:
self.parent_item_group = 'All Item Groups'
self.make_route()
def on_update(self):

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) {
frm.custom_make_buttons = {
'Stock Entry': 'Issue Material',
'Pick List': 'Pick List',
'Purchase Order': 'Purchase Order',
'Request for Quotation': 'Request for 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 (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") {
add_create_pick_list_button();
frm.add_custom_button(__("Transfer Material"),
() => 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) {
frappe.call({
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))
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': [
{
'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'),

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

@ -329,6 +329,11 @@ class TestPurchaseReceipt(unittest.TestCase):
location = frappe.db.get_value('Serial No', serial_nos[0].name, 'location')
self.assertEquals(location, "Test Location")
frappe.db.set_value("Asset", asset, "purchase_receipt", "")
frappe.db.set_value("Purchase Receipt Item", pr.items[0].name, "asset", "")
pr.load_from_db()
pr.cancel()
serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
self.assertEquals(len(serial_nos), 0)

View File

@ -17,6 +17,7 @@
"purchase_order",
"delivery_note_no",
"sales_invoice_no",
"pick_list",
"purchase_receipt_no",
"col2",
"posting_date",
@ -613,12 +614,19 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "pick_list",
"fieldtype": "Link",
"label": "Pick List",
"options": "Pick List",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2019-07-14 17:41:39.257508",
"modified": "2019-08-22 17:11:42.074154",
"modified_by": "Administrator",
"module": "Stock",
"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_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
backflush_raw_materials_based_on == 'BOM'):
purchase_order = frappe.get_doc("Purchase Order", self.purchase_order)
for se_item in self.items:
item_code = se_item.original_item or se_item.item_code
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 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}")
.format(se_item.item_code, self.purchase_order))
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.subcontracted_item = item_dict[d].get("main_item_code")
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"):
se_child.idx = item_dict[d].get("idx")
@ -1161,7 +1168,14 @@ class StockEntry(StockController):
where po.name = poitemsup.parent
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"):
item_code = d.get('original_item') or d.get('item_code')
reserve_warehouse = item_wh.get(item_code)

View File

@ -59,6 +59,7 @@
"reference_section",
"against_stock_entry",
"ste_detail",
"po_detail",
"column_break_51",
"transferred_qty",
"reference_purchase_receipt",
@ -480,11 +481,20 @@
"label": "Project",
"options": "Project",
"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,
"istable": 1,
"modified": "2019-07-12 11:34:53.190749",
"modified": "2019-08-20 14:01:02.319754",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Detail",

View File

@ -152,7 +152,6 @@ class TestStockReconciliation(unittest.TestCase):
for d in to_delete_records:
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
frappe.delete_doc("Stock Reconciliation", stock_doc.name)
for d in serial_nos + serial_nos1:
if frappe.db.exists("Serial No", d):
@ -203,9 +202,6 @@ class TestStockReconciliation(unittest.TestCase):
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
frappe.delete_doc("Batch", sr.items[0].batch_no)
for d in to_delete_records:
frappe.delete_doc("Stock Reconciliation", d)
def insert_existing_sle():
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry

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']
@frappe.whitelist()
def get_item_details(args, doc=None):
def get_item_details(args, doc=None, overwrite_warehouse=True):
"""
args = {
"item_code": "",
@ -44,11 +44,12 @@ def get_item_details(args, doc=None):
"set_warehouse": ""
}
"""
args = process_args(args)
item = frappe.get_cached_doc("Item", args.item_code)
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)
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))
def get_basic_details(args, item):
def get_basic_details(args, item, overwrite_warehouse=True):
"""
:param args: {
"item_code": "",
@ -225,15 +226,27 @@ def get_basic_details(args, item):
item_group_defaults = get_item_group_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
item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse)
if overwrite_warehouse or not 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:
defaults = frappe.defaults.get_defaults() or {}
if defaults.get("default_warehouse") and frappe.db.exists("Warehouse",
{'name': defaults.default_warehouse, 'company': args.company}):
warehouse_exists = frappe.db.exists("Warehouse", {
'name': 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'):
args['material_request_type'] = frappe.db.get_value('Material Request',
args.get('name'), 'material_request_type', cache=True)

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