diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a7ae6e1f7c9..2de394c155f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '7.2.21' +__version__ = '7.2.22' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 013036651ba..79a8f2b78a9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -28,7 +28,9 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("party_type", function() { return{ - query: "erpnext.setup.doctype.party_type.party_type.get_party_type" + "filters": { + "name": ["in",["Customer","Supplier"]], + } } }); diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index a8d166eef11..8aedf78fefc 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -7,7 +7,7 @@ import frappe import datetime from frappe import _, msgprint, scrub from frappe.defaults import get_user_permissions -from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, add_years +from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, add_years, get_timestamp from frappe.geo.doctype.address.address import get_address_display, get_default_address from frappe.email.doctype.contact.contact import get_contact_details, get_default_contact from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency @@ -353,8 +353,17 @@ def validate_party_frozen_disabled(party_type, party_name): def get_timeline_data(doctype, name): '''returns timeline data for the past one year''' from frappe.desk.form.load import get_communication_data + + out = {} data = get_communication_data(doctype, name, - fields = 'unix_timestamp(date(creation)), count(name)', - after = add_years(None, -1).strftime('%Y-%m-%d'), limit = 366, + fields = 'date(creation), count(name)', + after = add_years(None, -1).strftime('%Y-%m-%d'), group_by='group by date(creation)', as_dict=False) - return dict(data) \ No newline at end of file + + timeline_items = dict(data) + + for date, count in timeline_items.iteritems(): + timestamp = get_timestamp(date) + out.update({ timestamp: count }) + + return out \ No newline at end of file diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index c4ba9e7b1dd..1d1b4e21989 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -178,6 +178,9 @@ class BuyingController(StockController): for item in self.get("items"): item.rm_supp_cost = 0.0 + if self.is_subcontracted == "No" and self.get("supplied_items"): + self.set('supplied_items', []) + def update_raw_materials_supplied(self, item, raw_material_table): bom_items = self.get_items_from_bom(item.item_code, item.bom) raw_materials_cost = 0 diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e041a2b5178..d4ba1193e9c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -370,4 +370,5 @@ erpnext.patches.v7_2.set_null_value_to_fields erpnext.patches.v7_2.update_guardian_name_in_student_master erpnext.patches.v7_2.update_abbr_in_salary_slips erpnext.patches.v7_2.rename_evaluation_criteria -erpnext.patches.v7_2.update_party_type \ No newline at end of file +erpnext.patches.v7_2.update_party_type +erpnext.patches.v7_2.empty_supplied_items_for_non_subcontracted diff --git a/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py b/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py new file mode 100644 index 00000000000..ec6f8afc3a5 --- /dev/null +++ b/erpnext/patches/v7_2/empty_supplied_items_for_non_subcontracted.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + for doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt"]: + child_table = 'Purchase Receipt Item Supplied' if doctype != 'Purchase Order' else 'Purchase Order Item Supplied' + for data in frappe.db.sql(""" select distinct `tab{doctype}`.name from `tab{doctype}` , `tab{child_table}` + where `tab{doctype}`.name = `tab{child_table}`.parent and `tab{doctype}`.docstatus != 2 + and `tab{doctype}`.is_subcontracted = 'No' """.format(doctype = doctype, child_table = child_table), as_dict=1): + frappe.db.sql(""" delete from `tab{child_table}` + where parent = %s and parenttype = %s""".format(child_table= child_table), (data.name, doctype)) \ No newline at end of file diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 56bc27e1199..658c4fe00d2 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -100,7 +100,12 @@ function load_erpnext_slides() { fy = ["01-01", "12-31"]; next_year = current_year; } - + + var year_start_date = current_year + "-" + fy[0]; + if(year_start_date > get_today()) { + next_year = current_year + current_year -= 1; + } slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]); slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]); } diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 03c23d2acc0..439c729a91c 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -4,6 +4,13 @@ frappe.provide("erpnext.item"); frappe.ui.form.on("Item", { + setup: function(frm) { + frm.add_fetch('attribute', 'numeric_values', 'numeric_values'); + frm.add_fetch('attribute', 'from_range', 'from_range'); + frm.add_fetch('attribute', 'to_range', 'to_range'); + frm.add_fetch('attribute', 'increment', 'increment'); + frm.add_fetch('tax_type', 'tax_rate', 'tax_rate'); + }, onload: function(frm) { erpnext.item.setup_queries(frm); if (frm.doc.variant_of){ @@ -16,7 +23,6 @@ frappe.ui.form.on("Item", { }, refresh: function(frm) { - if(frm.doc.is_stock_item) { frm.add_custom_button(__("Balance"), function() { frappe.route_options = { @@ -54,9 +60,9 @@ frappe.ui.form.on("Item", { }, __("View")); frm.add_custom_button(__("Variant"), function() { - erpnext.item.make_variant() + erpnext.item.make_variant(frm); }, __("Make")); - cur_frm.page.set_inner_btn_group_as_primary(__("Make")); + frm.page.set_inner_btn_group_as_primary(__("Make")); } if (frm.doc.variant_of) { frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", [frm.doc.variant_of]), true); @@ -79,6 +85,17 @@ frappe.ui.form.on("Item", { frm.toggle_enable("is_fixed_asset", (frm.doc.__islocal || (!frm.doc.is_stock_item && ((frm.doc.__onload && frm.doc.__onload.asset_exists) ? false : true)))); + + frm.add_custom_button(__('Duplicate'), function() { + var new_item = frappe.model.copy_doc(frm.doc); + if(new_item.item_name===new_item.item_code) { + new_item.item_name = null; + } + if(new_item.description===new_item.description) { + new_item.description = null; + } + frappe.set_route('Form', 'Item', new_item.name); + }); }, validate: function(frm){ @@ -218,7 +235,8 @@ $.extend(erpnext.item, { return; frappe.require('assets/js/item-dashboard.min.js', function() { - var section = frm.dashboard.add_section('
' + __("Stock Levels") + '
'); + var section = frm.dashboard.add_section('
\ + ' + __("Stock Levels") + '
'); erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({ parent: section, item_code: frm.doc.name @@ -240,12 +258,12 @@ $.extend(erpnext.item, { } }, - make_variant: function(doc) { + make_variant: function(frm) { var fields = [] - for(var i=0;i< cur_frm.doc.attributes.length;i++){ + for(var i=0;i< frm.doc.attributes.length;i++){ var fieldtype, desc; - var row = cur_frm.doc.attributes[i]; + var row = frm.doc.attributes[i]; if (row.numeric_values){ fieldtype = "Float"; desc = "Min Value: "+ row.from_range +" , Max Value: "+ row.to_range +", in Increments of: "+ row.increment @@ -274,7 +292,7 @@ $.extend(erpnext.item, { frappe.call({ method:"erpnext.controllers.item_variant.get_variant", args: { - "template": cur_frm.doc.name, + "template": frm.doc.name, "args": d.get_values() }, callback: function(r) { @@ -296,7 +314,7 @@ $.extend(erpnext.item, { frappe.call({ method:"erpnext.controllers.item_variant.create_variant", args: { - "item": cur_frm.doc.name, + "item": frm.doc.name, "args": d.get_values() }, callback: function(r) { @@ -363,10 +381,3 @@ $.extend(erpnext.item, { frm.fields_dict.attributes.grid.toggle_enable("attribute_value", !frm.doc.variant_of); } }); - - -cur_frm.add_fetch('attribute', 'numeric_values', 'numeric_values'); -cur_frm.add_fetch('attribute', 'from_range', 'from_range'); -cur_frm.add_fetch('attribute', 'to_range', 'to_range'); -cur_frm.add_fetch('attribute', 'increment', 'increment'); -cur_frm.add_fetch('tax_type', 'tax_rate', 'tax_rate'); diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index ea4a94696c4..4a5094e34b0 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -90,7 +90,7 @@ "in_standard_filter": 0, "label": "Item Code", "length": 0, - "no_copy": 1, + "no_copy": 0, "oldfieldname": "item_code", "oldfieldtype": "Data", "permlevel": 0, @@ -138,7 +138,7 @@ }, { "allow_on_submit": 0, - "bold": 0, + "bold": 1, "collapsible": 0, "columns": 0, "fieldname": "item_name", @@ -161,7 +161,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 1, "set_only_once": 0, "unique": 0 diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d20ba497637..0f0205bae1a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -7,7 +7,7 @@ import erpnext import json import itertools from frappe import msgprint, _ -from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate, strip +from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate, strip, get_timestamp from frappe.website.website_generator import WebsiteGenerator from erpnext.setup.doctype.item_group.item_group import invalidate_cache_for, get_parent_item_groups from frappe.website.render import clear_cache @@ -63,6 +63,9 @@ class Item(WebsiteGenerator): def validate(self): super(Item, self).validate() + if not self.item_name: + self.item_name = self.item_code + if not self.description: self.description = self.item_name @@ -470,12 +473,12 @@ class Item(WebsiteGenerator): def check_if_linked_document_exists(self, key): linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "Purchase Receipt Item", "Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"] - - # For "Is Stock Item", following doctypes is important + + # For "Is Stock Item", following doctypes is important # because reserved_qty, ordered_qty and requested_qty updated from these doctypes if key == "is_stock_item": linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] - + for doctype in linked_doctypes: if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ frappe.db.get_value("Production Order", @@ -667,10 +670,17 @@ class Item(WebsiteGenerator): def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' - return dict(frappe.db.sql('''select unix_timestamp(posting_date), count(*) + out = {} + items = dict(frappe.db.sql('''select posting_date, count(*) from `tabStock Ledger Entry` where item_code=%s and posting_date > date_sub(curdate(), interval 1 year) group by posting_date''', name)) + + for date, count in items.iteritems(): + timestamp = get_timestamp(date) + out.update({ timestamp: count }) + + return out def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1): if (not end_of_life) or (disabled is None): diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 6ed2a36f295..0bd00093ac8 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -71,6 +71,34 @@ "set_only_once": 0, "unique": 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, + "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, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -138,7 +166,6 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "", @@ -156,6 +183,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "currency", + "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": "Currency", + "length": 0, + "no_copy": 0, + "options": "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, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -166,7 +222,7 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 0, + "in_filter": 1, "in_global_search": 1, "in_list_view": 0, "in_standard_filter": 1, @@ -197,8 +253,8 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, + "in_filter": 1, + "in_global_search": 1, "in_list_view": 1, "in_standard_filter": 0, "label": "Rate", @@ -218,35 +274,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "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": "Currency", - "length": 0, - "no_copy": 0, - "options": "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, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -285,7 +312,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_global_search": 1, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Item Name", @@ -329,63 +356,6 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "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, - "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, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bulk_import_help", - "fieldtype": "HTML", - "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": "Bulk Import Help", - "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, - "unique": 0 } ], "hide_heading": 0, @@ -446,7 +416,7 @@ "write": 1 } ], - "quick_entry": 0, + "quick_entry": 1, "read_only": 0, "read_only_onload": 0, "show_name_in_global_search": 0, diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index c1405c374fd..81dbdc98471 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -9,47 +9,50 @@ from frappe.utils import cint, formatdate @frappe.whitelist(allow_guest=True) def send_message(subject="Website Query", message="", sender="", status="Open"): - from frappe.www.contact import send_message as website_send_message - lead = customer = None + from frappe.www.contact import send_message as website_send_message + lead = customer = None - website_send_message(subject, message, sender) + website_send_message(subject, message, sender) - customer = frappe.db.get_value('Contact', dict(email_id=sender), 'customer') - if not customer: - lead = frappe.db.get_value('Lead', dict(email_id=sender)) - if not lead: - new_lead = frappe.get_doc(dict( - doctype='Lead', - email_id = sender, - lead_name = sender.split('@')[0].title() - )).insert(ignore_permissions=True) + customer = frappe.db.sql("""select distinct dl.link_name from `tabDynamic Link` dl + left join `tabContact` c on dl.parent=c.name where dl.link_doctype='Customer' + and c.email_id='{email_id}'""".format(email_id=sender)) - opportunity = frappe.get_doc(dict( - doctype='Opportunity', - enquiry_from = 'Customer' if customer else 'Lead', - status = 'Open', - title = subject, - to_discuss=message - )) + if not customer: + lead = frappe.db.get_value('Lead', dict(email_id=sender)) + if not lead: + new_lead = frappe.get_doc(dict( + doctype='Lead', + email_id = sender, + lead_name = sender.split('@')[0].title() + )).insert(ignore_permissions=True) - if customer: - opportunity.customer = customer - elif lead: - opportunity.lead = lead - else: - opportunity.lead = new_lead.name + opportunity = frappe.get_doc(dict( + doctype='Opportunity', + enquiry_from = 'Customer' if customer else 'Lead', + status = 'Open', + title = subject, + to_discuss=message + )) - opportunity.insert(ignore_permissions=True) + if customer: + opportunity.customer = customer[0][0] + elif lead: + opportunity.lead = lead + else: + opportunity.lead = new_lead.name - comm = frappe.get_doc({ - "doctype":"Communication", - "subject": subject, - "content": message, - "sender": sender, - "sent_or_received": "Received", - 'reference_doctype': 'Opportunity', - 'reference_name': opportunity.name - }) - comm.insert(ignore_permissions=True) + opportunity.insert(ignore_permissions=True) - return "okay" + comm = frappe.get_doc({ + "doctype":"Communication", + "subject": subject, + "content": message, + "sender": sender, + "sent_or_received": "Received", + 'reference_doctype': 'Opportunity', + 'reference_name': opportunity.name + }) + comm.insert(ignore_permissions=True) + + return "okay"