From 637915f295d4879e90ef30cbc6b9ab12820ae5af Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 28 Jan 2020 13:11:49 +0530 Subject: [PATCH 01/74] fix: odometer value was not syncing properly --- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 38 +- .../hr/doctype/vehicle_log/vehicle_log.json | 842 ++++-------------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 18 +- 3 files changed, 201 insertions(+), 697 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 7694cfed7cc..4c192a0234b 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -2,19 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on("Vehicle Log", { - refresh: function(frm,cdt,cdn) { - var vehicle_log=frappe.model.get_doc(cdt,cdn); - if (vehicle_log.license_plate) { - frappe.call({ - method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", - args: { - license_plate: vehicle_log.license_plate - }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, ("model"), r.message[0]); - frappe.model.set_value(cdt, cdn, ("make"), r.message[1]); - } - }) + refresh: function(frm) { + + if(frm.doc.license_plate && frm.doc.__islocal){ + frm.events.set_vehicle_details(frm) } if(frm.doc.docstatus == 1) { @@ -25,6 +16,27 @@ frappe.ui.form.on("Vehicle Log", { } }, + license_plate: function(frm){ + if(frm.doc.license_plate){ + frm.events.set_vehicle_details(frm) + } + }, + + set_vehicle_details: function(frm){ + frappe.call({ + method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", + args: { + license_plate: frm.doc.license_plate + }, + callback: function(r) { + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[0]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[1]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); + } + }); + }, + expense_claim: function(frm){ frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json index cde39e7ee44..52effffc064 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.json @@ -1,706 +1,192 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2016-09-03 14:14:51.788550", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "autoname": "naming_series:", + "creation": "2016-09-03 14:14:51.788550", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "vehicle_section", + "naming_series", + "license_plate", + "employee", + "column_break_4", + "column_break_7", + "model", + "make", + "odometer_reading", + "date", + "odometer", + "column_break_12", + "last_odometer", + "refuelling_details", + "fuel_qty", + "price", + "column_break_15", + "supplier", + "invoice", + "service_details", + "service_detail", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_section", - "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, - "options": "fa fa-user", - "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 - }, + "fieldname": "vehicle_section", + "fieldtype": "Section Break", + "options": "fa fa-user" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "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": "Series", - "length": 0, - "no_copy": 1, - "options": "HR-VLOG-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-VLOG-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "license_plate", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "License Plate", - "length": 0, - "no_copy": 0, - "options": "Vehicle", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "license_plate", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "License Plate", + "options": "Vehicle", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "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": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "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 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "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 - }, + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "model", - "fieldtype": "Read Only", - "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": "Model", - "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 - }, + "fieldname": "model", + "fieldtype": "Read Only", + "label": "Model" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "make", - "fieldtype": "Read Only", - "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": "Make", - "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 - }, + "fieldname": "make", + "fieldtype": "Read Only", + "label": "Make" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "odometer_reading", - "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": "Odometer Reading", - "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 - }, + "fieldname": "odometer_reading", + "fieldtype": "Section Break", + "label": "Odometer Reading" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "date", - "fieldtype": "Date", - "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": "Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "odometer", - "fieldtype": "Int", - "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": "Odometer", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "odometer", + "fieldtype": "Int", + "label": "Current Odometer value ", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "refuelling_details", - "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": "Refuelling Details", - "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 - }, + "collapsible": 1, + "fieldname": "refuelling_details", + "fieldtype": "Section Break", + "label": "Refuelling Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fuel_qty", - "fieldtype": "Float", - "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": "Fuel Qty", - "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 - }, + "fieldname": "fuel_qty", + "fieldtype": "Float", + "label": "Fuel Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price", - "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": "Fuel Price", - "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 - }, + "fieldname": "price", + "fieldtype": "Currency", + "label": "Fuel Price" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_15", - "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 - }, + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier", - "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": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "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 - }, + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoice", - "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": "Invoice Ref", - "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 - }, + "fieldname": "invoice", + "fieldtype": "Data", + "label": "Invoice Ref" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "service_details", - "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": "Service Details", - "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 - }, + "collapsible": 1, + "fieldname": "service_details", + "fieldtype": "Section Break", + "label": "Service Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "service_detail", - "fieldtype": "Table", - "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": "Service Detail", - "length": 0, - "no_copy": 0, - "options": "Vehicle Service", - "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 - }, + "fieldname": "service_detail", + "fieldtype": "Table", + "label": "Service Detail", + "options": "Vehicle Service" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "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": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Vehicle Log", - "permlevel": 0, - "print_hide": 1, - "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 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Vehicle Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "last_odometer", + "fieldtype": "Int", + "label": "last Odometer Value ", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:51.131186", - "modified_by": "Administrator", - "module": "HR", - "name": "Vehicle Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-01-28 12:43:34.419647", + "modified_by": "Administrator", + "module": "HR", + "name": "Vehicle Log", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Fleet Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Fleet Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index df633611be8..0dbf1ceb961 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -11,22 +11,28 @@ from frappe.model.document import Document class VehicleLog(Document): def validate(self): - last_odometer=frappe.db.get_value("Vehicle", self.license_plate, "last_odometer") - if flt(self.odometer) < flt(last_odometer): - frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(last_odometer)) + if flt(self.odometer) < flt(self.last_odometer): + frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer)) for service_detail in self.service_detail: if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) def on_submit(self): - frappe.db.sql("update `tabVehicle` set last_odometer=%s where license_plate=%s", - (self.odometer, self.license_plate)) + print("I am here") + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) + + def on_cancel(self): + print("sel"*10, self.last_odometer, self.odometer) + distance_travelled = self.odometer - self.last_odometer + if(distance_travelled > 0): + updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled + frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value) @frappe.whitelist() def get_make_model(license_plate): vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make,vehicle.model) + return (vehicle.make,vehicle.model,vehicle.last_odometer,vehicle.employee) @frappe.whitelist() def make_expense_claim(docname): From ce598530dbf04ac0fb51b46a9d508e0ad2c30c5c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 5 Feb 2020 12:45:43 +0530 Subject: [PATCH 02/74] fix: requested changes --- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 14 +++++++------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 4c192a0234b..bdb37d2b734 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -5,32 +5,32 @@ frappe.ui.form.on("Vehicle Log", { refresh: function(frm) { if(frm.doc.license_plate && frm.doc.__islocal){ - frm.events.set_vehicle_details(frm) + frm.events.set_vehicle_details(frm); } if(frm.doc.docstatus == 1) { frm.add_custom_button(__('Expense Claim'), function() { - frm.events.expense_claim(frm) + frm.events.expense_claim(frm); }, __('Create')); frm.page.set_inner_btn_group_as_primary(__('Create')); } }, - license_plate: function(frm){ + license_plate: function(frm) { if(frm.doc.license_plate){ - frm.events.set_vehicle_details(frm) + frm.events.set_vehicle_details(frm); } }, - set_vehicle_details: function(frm){ + set_vehicle_details: function(frm) { frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model", args: { license_plate: frm.doc.license_plate }, callback: function(r) { - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[0]); - frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[1]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]); + frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]); frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]); frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]); } diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index 0dbf1ceb961..dfdfc0d7202 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -18,12 +18,17 @@ class VehicleLog(Document): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) + def before_save(self): + model_details = get_make_model(self.license_plate) + self.make = model_details[0] + self.model = model_details[1] + self.last_odometer = model_details[2] + self.employee = model_details[3] + def on_submit(self): - print("I am here") frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer) def on_cancel(self): - print("sel"*10, self.last_odometer, self.odometer) distance_travelled = self.odometer - self.last_odometer if(distance_travelled > 0): updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled @@ -32,7 +37,7 @@ class VehicleLog(Document): @frappe.whitelist() def get_make_model(license_plate): vehicle=frappe.get_doc("Vehicle",license_plate) - return (vehicle.make,vehicle.model,vehicle.last_odometer,vehicle.employee) + return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee) @frappe.whitelist() def make_expense_claim(docname): From 031d4092d0e20e4567b56a2b69283bb6ea0630a5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 19 Feb 2020 16:46:00 +0530 Subject: [PATCH 03/74] test: syncing of odometer value --- .../doctype/vehicle_log/test_vehicle_log.py | 60 ++++++++++++------- erpnext/hr/doctype/vehicle_log/vehicle_log.py | 2 +- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py index 35400b0ca83..3770da73fcf 100644 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py @@ -8,25 +8,9 @@ import unittest from frappe.utils import nowdate,flt, cstr,random_string # test_records = frappe.get_test_records('Vehicle Log') class TestVehicleLog(unittest.TestCase): - def test_make_vehicle_log(self): - license_plate=random_string(10).upper() + def test_make_vehicle_log_and_syncing_of_odometer_value(self): employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] - vehicle = frappe.get_doc({ - "doctype": "Vehicle", - "license_plate": cstr(license_plate), - "make": "Maruti", - "model": "PCM", - "last_odometer":5000, - "acquisition_date":frappe.utils.nowdate(), - "location": "Mumbai", - "chassis_no": "1234ABCD", - "uom": "Litre", - "vehicle_value":frappe.utils.flt(500000) - }) - try: - vehicle.insert() - except frappe.DuplicateEntryError: - pass + license_plate = get_vehicle(employee_id) vehicle_log = frappe.get_doc({ "doctype": "Vehicle Log", "license_plate": cstr(license_plate), @@ -36,5 +20,41 @@ class TestVehicleLog(unittest.TestCase): "fuel_qty":frappe.utils.flt(50), "price": frappe.utils.flt(500) }) - vehicle_log.insert() - vehicle_log.submit() \ No newline at end of file + vehicle_log.save() + vehicle_log.submit() + + #checking value of vehicle odometer value on submit. + vehicle = frappe.get_doc("Vehicle", license_plate) + self.assertEqual(vehicle.last_odometer, vehicle_log.odometer) + + #checking value vehicle odometer on vehicle log cancellation. + last_odometer = vehicle_log.last_odometer + current_odometer = vehicle_log.odometer + distance_travelled = current_odometer - last_odometer + + vehicle_log.cancel() + vehicle.reload() + + self.assertEqual(vehicle.last_odometer, current_odometer - distance_travelled) + + +def get_vehicle(employee_id): + license_plate=random_string(10).upper() + vehicle = frappe.get_doc({ + "doctype": "Vehicle", + "license_plate": cstr(license_plate), + "make": "Maruti", + "model": "PCM", + "employee": employee_id, + "last_odometer":5000, + "acquisition_date":frappe.utils.nowdate(), + "location": "Mumbai", + "chassis_no": "1234ABCD", + "uom": "Litre", + "vehicle_value":frappe.utils.flt(500000) + }) + try: + vehicle.insert() + except frappe.DuplicateEntryError: + pass + return license_plate \ No newline at end of file diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.py b/erpnext/hr/doctype/vehicle_log/vehicle_log.py index dfdfc0d7202..12cc1dd03a6 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.py @@ -18,7 +18,7 @@ class VehicleLog(Document): if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount): frappe.throw(_("Service Item,Type,frequency and expense amount are required")) - def before_save(self): + def before_insert(self): model_details = get_make_model(self.license_plate) self.make = model_details[0] self.model = model_details[1] From 1beeb2890881740f4747ddbd3bd0a8a4c311e03a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:22:33 +0530 Subject: [PATCH 04/74] fix: check for available stock in product bundle's website warehouse (#20680) --- erpnext/utilities/product.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 1c0d4c38c73..c23c1f7096d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -129,6 +129,7 @@ def get_non_stock_item_status(item_code, item_warehouse_field): #if item belongs to product bundle, check if bundle items are in stock if frappe.db.exists("Product Bundle", item_code): items = frappe.get_doc("Product Bundle", item_code).get_all_children() - return all([ get_qty_in_stock(d.item_code, item_warehouse_field).in_stock for d in items ]) + bundle_warehouse = frappe.db.get_value('Item', item_code, item_warehouse_field) + return all([ get_qty_in_stock(d.item_code, item_warehouse_field, bundle_warehouse).in_stock for d in items ]) else: return 1 From 8e80c17602663ef78e3be16a187520fa148f866a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:32:09 +0530 Subject: [PATCH 05/74] chore: SINV set_status remove redundant function calls (#20661) --- .../accounts/doctype/sales_invoice/sales_invoice.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0dc889c0b7a..0b793e847a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1237,25 +1237,28 @@ class SalesInvoice(SellingController): precision = self.precision("outstanding_amount") outstanding_amount = flt(self.outstanding_amount, precision) + due_date = getdate(self.due_date) + nowdate = getdate() + discountng_status = self.get_discounting_status() if not status: if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': self.status = "Overdue and Discounted" - elif outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): + elif outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" - elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': self.status = "Unpaid and Discounted" - elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): + elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" #Check if outstanding amount is 0 due to credit note issued against invoice elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Credit Note Issued" elif self.is_return == 1: self.status = "Return" - elif outstanding_amount <=0: + elif outstanding_amount<=0: self.status = "Paid" else: self.status = "Submitted" From ae9159fbd83e8c40411dd75505d7863a7c3627e2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 20 Feb 2020 12:33:07 +0530 Subject: [PATCH 06/74] fix: mandatory on hold comment for purchase invoice (#20667) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d4676ff0375..737c18a8a98 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1015,7 +1015,7 @@ def unblock_invoice(name): @frappe.whitelist() -def block_invoice(name, hold_comment, release_date): +def block_invoice(name, release_date, hold_comment=None): if frappe.db.exists('Purchase Invoice', name): pi = frappe.get_doc('Purchase Invoice', name) pi.block_invoice(hold_comment, release_date) From 4e2b9395b957bbc5d8d3c560006b9259f593d8c7 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 20 Feb 2020 12:48:32 +0530 Subject: [PATCH 07/74] chore: drop cypress requirement (#20675) --- cypress.json | 3 - cypress/fixtures/example.json | 5 - .../opportunity/lost_reason_detail.js | 31 - cypress/plugins/index.js | 17 - cypress/support/commands.js | 25 - cypress/support/index.js | 22 - package.json | 13 +- yarn.lock | 1302 ----------------- 8 files changed, 11 insertions(+), 1407 deletions(-) delete mode 100644 cypress.json delete mode 100644 cypress/fixtures/example.json delete mode 100644 cypress/integration/opportunity/lost_reason_detail.js delete mode 100644 cypress/plugins/index.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 2be29158592..00000000000 --- a/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseUrl": "http://test_site_ui:8000" -} \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d9352a1..00000000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/cypress/integration/opportunity/lost_reason_detail.js b/cypress/integration/opportunity/lost_reason_detail.js deleted file mode 100644 index 9cf204889d6..00000000000 --- a/cypress/integration/opportunity/lost_reason_detail.js +++ /dev/null @@ -1,31 +0,0 @@ -context('Form', () => { - before(() => { - cy.login('Administrator', 'qwe'); - cy.visit('/desk'); - }); - - it('create a new opportunity', () => { - cy.visit('/desk#Form/Opportunity/New Opportunity 1'); - cy.get('.page-title').should('contain', 'Not Saved'); - cy.fill_field('opportunity_from', 'Customer', 'Select'); - cy.fill_field('party_name', 'Test Customer', 'Link').blur(); - cy.get('.primary-action').click(); - cy.get('.page-title').should('contain', 'Open'); - cy.get('.form-inner-toolbar button:contains("Lost")').click({ force: true }); - cy.get('.modal input[data-fieldname="lost_reason"]').as('input'); - cy.get('@input').focus().type('Higher', { delay: 200 }); - cy.get('.modal .awesomplete ul') - .should('be.visible') - .get('li:contains("Higher Price")') - .click({ force: true }); - cy.get('@input').focus().type('No Followup', { delay: 200 }); - cy.get('.modal .awesomplete ul') - .should('be.visible') - .get('li:contains("No Followup")') - .click(); - - cy.fill_field('detailed_reason', 'Test Detailed Reason', 'Text'); - cy.get('.modal button:contains("Declare Lost")').click({ force: true }); - cy.get('.page-title').should('contain', 'Lost'); - }); -}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index cd02c3bd7a8..00000000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -// module.exports = (on, config) => { -// `on` is used to hook into various events Cypress emits -// `config` is the resolved Cypress config -// } diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index c1f5a772e2b..00000000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index 872df2d5d60..00000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,22 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// import frappe commands -import '../../../frappe/cypress/support/index'; -// Import commands.js using ES2015 syntax: -import './commands'; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/package.json b/package.json index 0e09cebf56e..9701bf3a21e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,14 @@ { - "dependencies": { - "cypress": "^3.1.4" + "name": "erpnext", + "description": "Open Source ERP System powered by the Frappe Framework", + "repository": { + "type": "git", + "url": "git+https://github.com/frappe/erpnext.git" + }, + "homepage": "https://erpnext.com", + "author": "Frappe Technologies Pvt. Ltd.", + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/frappe/erpnext/issues" } } diff --git a/yarn.lock b/yarn.lock index 6f1ff10839c..fb57ccd13af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,1305 +2,3 @@ # yarn lockfile v1 -"@cypress/listr-verbose-renderer@0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" - integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -"@cypress/xvfb@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.3.tgz#6319afdcdcff7d1505daeeaa84484d0596189860" - integrity sha512-yYrK+/bgL3hwoRHMZG4r5fyLniCy1pXex5fimtewAY6vE/jsVs8Q37UsEO03tFlcmiLnQ3rBNMaZBYTi/+C1cw== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - -"@types/blob-util@1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a" - integrity sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w== - -"@types/bluebird@3.5.18": - version "3.5.18" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6" - integrity sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w== - -"@types/chai-jquery@1.1.35": - version "1.1.35" - resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04" - integrity sha512-7aIt9QMRdxuagLLI48dPz96YJdhu64p6FCa6n4qkGN5DQLHnrIjZpD9bXCvV2G0NwgZ1FAmfP214dxc5zNCfgQ== - dependencies: - "@types/chai" "*" - "@types/jquery" "*" - -"@types/chai@*": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" - integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA== - -"@types/chai@4.0.8": - version "4.0.8" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" - integrity sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg== - -"@types/jquery@*": - version "3.3.29" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" - integrity sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ== - dependencies: - "@types/sizzle" "*" - -"@types/jquery@3.3.6": - version "3.3.6" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.6.tgz#5932ead926307ca21e5b36808257f7c926b06565" - integrity sha512-403D4wN95Mtzt2EoQHARf5oe/jEPhzBOBNrunk+ydQGW8WmkQ/E8rViRAEB1qEt/vssfGfNVD6ujP4FVeegrLg== - -"@types/lodash@4.14.87": - version "4.14.87" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b" - integrity sha512-AqRC+aEF4N0LuNHtcjKtvF9OTfqZI0iaBoe3dA6m/W+/YZJBZjBmW/QIZ8fBeXC6cnytSY9tBoFBqZ9uSCeVsw== - -"@types/minimatch@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/mocha@2.2.44": - version "2.2.44" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" - integrity sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw== - -"@types/sinon-chai@2.7.29": - version "2.7.29" - resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-2.7.29.tgz#4db01497e2dd1908b2bd30d1782f456353f5f723" - integrity sha512-EkI/ZvJT4hglWo7Ipf9SX+J+R9htNOMjW8xiOhce7+0csqvgoF5IXqY5Ae1GqRgNtWCuaywR5HjVa1snkTqpOw== - dependencies: - "@types/chai" "*" - "@types/sinon" "*" - -"@types/sinon@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.2.tgz#234c03dd39bfa97616b28215caea3de043c63310" - integrity sha512-YvJOqPk4kh1eQyxuASDD4MDK27XWAhtw6hJ7rRayEOkkTpZkqDWpDb4OjLVzFGdapOuUgZdnqO+71Q3utCJtcA== - -"@types/sinon@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.0.tgz#84e707e157ec17d3e4c2a137f41fc3f416c0551e" - integrity sha512-kcYoPw0uKioFVC/oOqafk2yizSceIQXCYnkYts9vJIwQklFRsMubTObTDrjQamUyBRd47332s85074cd/hCwxg== - -"@types/sizzle@*": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" - integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== - -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -async@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" - integrity sha1-SZAgDxjqW4N8LMT4wDGmmFw4VhE= - dependencies: - lodash "^4.14.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -babel-runtime@^6.18.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -bluebird@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" - integrity sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -cachedir@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" - integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg== - dependencies: - os-homedir "^1.0.1" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@2.4.1, chalk@^2.0.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -check-more-types@2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= - -ci-info@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= - -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= - dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -combined-stream@^1.0.6, combined-stream@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== - dependencies: - delayed-stream "~1.0.0" - -commander@2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== - -common-tags@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" - integrity sha1-EYe+Tz1M8MBCfUP3Tu8fc1AWFMA= - dependencies: - babel-runtime "^6.18.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-js@^2.4.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.1.tgz#87416ae817de957a3f249b3b5ca475d4aaed6042" - integrity sha512-L72mmmEayPJBejKIWe2pYtGis5r0tQ5NaJekdhyXgeMQTpJoBsH0NL4ElY2LfSoV15xeQWKQ+XTTOZdyero5Xg== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cypress@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.4.tgz#2af04da05e09f9d3871d05713b364472744c4216" - integrity sha512-8VJYtCAFqHXMnRDo4vdomR2CqfmhtReoplmbkXVspeKhKxU8WsZl0Nh5yeil8txxhq+YQwDrInItUqIm35Vw+g== - dependencies: - "@cypress/listr-verbose-renderer" "0.4.1" - "@cypress/xvfb" "1.2.3" - "@types/blob-util" "1.3.3" - "@types/bluebird" "3.5.18" - "@types/chai" "4.0.8" - "@types/chai-jquery" "1.1.35" - "@types/jquery" "3.3.6" - "@types/lodash" "4.14.87" - "@types/minimatch" "3.0.3" - "@types/mocha" "2.2.44" - "@types/sinon" "7.0.0" - "@types/sinon-chai" "2.7.29" - bluebird "3.5.0" - cachedir "1.3.0" - chalk "2.4.1" - check-more-types "2.24.0" - commander "2.11.0" - common-tags "1.4.0" - debug "3.1.0" - execa "0.10.0" - executable "4.1.1" - extract-zip "1.6.6" - fs-extra "4.0.1" - getos "3.1.0" - glob "7.1.2" - is-ci "1.0.10" - is-installed-globally "0.1.0" - lazy-ass "1.6.0" - listr "0.12.0" - lodash "4.17.11" - log-symbols "2.2.0" - minimist "1.2.0" - moment "2.22.2" - ramda "0.24.1" - request "2.87.0" - request-progress "0.3.1" - supports-color "5.1.0" - tmp "0.0.31" - url "0.11.0" - yauzl "2.8.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -execa@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -executable@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - -extend@~3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@1.6.6: - version "1.6.6" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" - integrity sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw= - dependencies: - concat-stream "1.6.0" - debug "2.6.9" - mkdirp "0.5.0" - yauzl "2.4.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= - dependencies: - pend "~1.2.0" - -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.1: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-extra@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" - integrity sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^3.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -getos@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.0.tgz#db3aa4df15a3295557ce5e81aa9e3e5cdfaa6567" - integrity sha512-i9vrxtDu5DlLVFcrbqUqGWYlZN/zZ4pGMICCAcZoYsX3JA54nYp8r5EThw5K+m2q3wszkx4Th746JstspB0H4Q== - dependencies: - async "2.4.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - dependencies: - ini "^1.3.4" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0= - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -is-ci@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" - integrity sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4= - dependencies: - ci-info "^1.0.0" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-installed-globally@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" - integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY= - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -lazy-ass@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= - -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" - integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -listr@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" - integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.2.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.0.0-beta.11" - stream-to-observable "^0.1.0" - strip-ansi "^3.0.1" - -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - -lodash@4.17.11, lodash@^4.14.0: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - -mime-types@^2.1.12, mime-types@~2.1.17: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== - dependencies: - mime-db "~1.37.0" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -mkdirp@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" - integrity sha1-HXMHam35hs2TROFecfzAWkyavxI= - dependencies: - minimist "0.0.8" - -moment@2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" - integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y= - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - -os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pify@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -qs@~6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -ramda@0.24.1: - version "0.24.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" - integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= - -readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -request-progress@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a" - integrity sha1-ByHBBdipasayzossia4tXs/Pazo= - dependencies: - throttleit "~0.0.2" - -request@2.87.0: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" - integrity sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -rxjs@^5.0.0-beta.11: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.5.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" - integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - -sshpk@^1.7.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de" - integrity sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stream-to-observable@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" - integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -supports-color@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" - integrity sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ== - dependencies: - has-flag "^2.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - -throttleit@~0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" - integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= - -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= - dependencies: - os-tmpdir "~1.0.1" - -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== - dependencies: - punycode "^1.4.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -url@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -yauzl@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" - integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= - dependencies: - fd-slicer "~1.0.1" - -yauzl@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.8.0.tgz#79450aff22b2a9c5a41ef54e02db907ccfbf9ee2" - integrity sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.0.1" From a6f56bbc3e7cd0e2fb470963975fdfe6bdc7190c Mon Sep 17 00:00:00 2001 From: Mathieu Brunot Date: Thu, 20 Feb 2020 08:20:09 +0100 Subject: [PATCH 08/74] chore(ci-coverage): Pin coverage 4.5.4 #20646 (#20648) * chore(ci-coverage): Pin coveralls 4.5.4 #20646 Signed-off-by: mathieu.brunot * chore: Pin coverage Signed-off-by: mathieu.brunot --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 365eb67f3dc..213445b806e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,5 +77,6 @@ install: - bench --site test_site reinstall --yes after_script: + - pip install coverage==4.5.4 - pip install python-coveralls - coveralls -b apps/erpnext -d ../../sites/.coverage From ab8aa43ffb18c5fdec4eaf618c203bb97e695201 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 20 Feb 2020 12:52:37 +0530 Subject: [PATCH 09/74] fix: changed field type which was affecting filters (#20670) --- erpnext/hr/doctype/attendance/attendance.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index bc89b368d30..20974a0b7d2 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -57,11 +57,12 @@ { "fetch_from": "employee.employee_name", "fieldname": "employee_name", - "fieldtype": "Read Only", + "fieldtype": "Data", "in_global_search": 1, "label": "Employee Name", "oldfieldname": "employee_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "read_only": 1 }, { "default": "Present", @@ -173,7 +174,7 @@ "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, - "modified": "2019-07-29 20:35:40.845422", + "modified": "2020-02-19 14:25:32.945842", "modified_by": "Administrator", "module": "HR", "name": "Attendance", From 355051bf75ced103449e67a4bdcefdd67b7f9f63 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 20 Feb 2020 12:59:21 +0530 Subject: [PATCH 10/74] fix: apply url encoding to project names (#20641) --- erpnext/templates/includes/projects/project_row.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 55b02e20045..73c83ef560b 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -1,6 +1,6 @@ {% if doc.status=="Open" %}
- +
From c74343531fe97b995c38b5a3806304397fb3ea12 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 20 Feb 2020 13:25:45 +0530 Subject: [PATCH 11/74] fix: skip earned leaves check for max leaves set to zero or less (#20536) * fix: skip earned leaves check for max leaves set to zero or less * test: earned leaves creation --- .../leave_application/test_leave_application.py | 10 +++++++++- erpnext/hr/utils.py | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 5dfcad6a294..fc18d7a4cab 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -409,7 +409,7 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21) - def test_earned_leave(self): + def test_earned_leaves_creation(self): leave_period = get_leave_period() employee = get_employee() leave_type = 'Test Earned Leave Type' @@ -437,6 +437,14 @@ class TestLeaveApplication(unittest.TestCase): i += 1 self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6) + # validate earned leaves creation without maximum leaves + frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0) + i = 0 + while(i<6): + allocate_earned_leaves() + i += 1 + self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9) + # test to not consider current leave in leave balance while submitting def test_current_leave_on_submit(self): employee = get_employee() diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 9b0ca4e9a1a..4f5653dc67e 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -316,7 +316,9 @@ def allocate_earned_leaves(): allocation = frappe.get_doc('Leave Allocation', allocation.name) new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) - new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed + + if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: + new_allocation = e_leave_type.max_leaves_allowed if new_allocation == allocation.total_leaves_allocated: continue From 588a89f95719bdfcb7acf5711114f9868469a409 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 20 Feb 2020 13:41:00 +0530 Subject: [PATCH 12/74] fix: return null for attribute (#20684) --- .../doctype/amazon_mws_settings/amazon_methods.py | 6 ++++++ .../doctype/amazon_mws_settings/amazon_mws_settings.py | 5 ++--- .../doctype/amazon_mws_settings/xml_utils.py | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index 2f39dc596bf..3bc8db5e78a 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -165,6 +165,9 @@ def create_item_code(amazon_item_json, sku): return item.name def create_manufacturer(amazon_item_json): + if not amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer: + return None + existing_manufacturer = frappe.db.get_value("Manufacturer", filters={"short_name":amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer}) @@ -177,6 +180,9 @@ def create_manufacturer(amazon_item_json): return existing_manufacturer def create_brand(amazon_item_json): + if not amazon_item_json.Product.AttributeSets.ItemAttributes.Brand: + return None + existing_brand = frappe.db.get_value("Brand", filters={"brand":amazon_item_json.Product.AttributeSets.ItemAttributes.Brand}) if not existing_brand: diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py index 249a73f9fbb..c222afbb6c1 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document import dateutil from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods import get_products_details, get_orders class AmazonMWSSettings(Document): def validate(self): @@ -19,12 +18,12 @@ class AmazonMWSSettings(Document): def get_products_details(self): if self.enable_amazon == 1: - get_products_details() + frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_products_details') def get_order_details(self): if self.enable_amazon == 1: after_date = dateutil.parser.parse(self.after_date).strftime("%Y-%m-%d") - get_orders(after_date = after_date) + frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_orders', after_date=after_date) def schedule_get_order_details(): mws_settings = frappe.get_doc("Amazon MWS Settings") diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py index 58db669411a..a25a29f9e5f 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py @@ -33,7 +33,10 @@ class object_dict(dict): def __getattr__(self, item): - d = self.__getitem__(item) + try: + d = self.__getitem__(item) + except KeyError: + return None if isinstance(d, dict) and 'value' in d and len(d) == 1: return d['value'] From df15c758f6abde9577924db779e0652c303406ce Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 20 Feb 2020 15:29:09 +0530 Subject: [PATCH 13/74] fix: Additional salary can be created only for active employee --- erpnext/hr/doctype/additional_salary/additional_salary.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.js b/erpnext/hr/doctype/additional_salary/additional_salary.js index d0f64ab51bb..18f6b8b52d5 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.js +++ b/erpnext/hr/doctype/additional_salary/additional_salary.js @@ -8,7 +8,8 @@ frappe.ui.form.on('Additional Salary', { frm.set_query("employee", function() { return { filters: { - company: frm.doc.company + company: frm.doc.company, + status: "Active" } }; }); From 9921d28ea7357be1eb1d2791bcfdd320fb0bf08f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 24 Feb 2020 18:45:47 +0530 Subject: [PATCH 14/74] fix: account dashboard not working --- .../account_balance_timeline.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index 43acded3a98..a9b3d7c4cc2 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, json from frappe import _ -from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate +from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form from erpnext.accounts.report.general_ledger.general_ledger import execute from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending @@ -30,8 +30,13 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d account = filters.get("account") company = filters.get("company") - if not account and chart: - frappe.throw(_("Account is not set for the dashboard chart {0}").format(chart)) + if not account and chart_name: + frappe.throw(_("Account is not set for the dashboard chart {0}") + .format(get_link_to_form("Dashboard Chart", chart_name))) + + if not frappe.db.exists("Account", account) and chart_name: + frappe.throw(_("Account {0} does not exists in the dashboard chart {1}") + .format(account, get_link_to_form("Dashboard Chart", chart_name))) if not to_date: to_date = nowdate() From a20bd89c20819a5ab6e6c31f03a3fab7efc29391 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 11:25:30 +0530 Subject: [PATCH 15/74] fix: reorder material request not created if doctype has custom mandatory field (#20720) --- erpnext/stock/reorder_item.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 39fb0240236..97776739a87 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -116,6 +116,8 @@ def create_material_request(material_requests): else: exceptions_list.append(frappe.get_traceback()) + frappe.log_error(frappe.get_traceback()) + for request_type in material_requests: for company in material_requests[request_type]: try: @@ -158,6 +160,7 @@ def create_material_request(material_requests): schedule_dates = [d.schedule_date for d in mr.items] mr.schedule_date = max(schedule_dates or [nowdate()]) + mr.flags.ignore_mandatory = True mr.insert() mr.submit() mr_list.append(mr) From 0aa7aa5996c090ef0cc050570324d80a81c23819 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 11:27:49 +0530 Subject: [PATCH 16/74] fix: rate and amount in material request copying from sales order (#20718) --- .../material_request_item/material_request_item.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 795971b5e3d..9d1dafb136b 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-02-22 01:28:02", "doctype": "DocType", @@ -185,12 +186,14 @@ { "fieldname": "rate", "fieldtype": "Currency", - "label": "Rate" + "label": "Rate", + "no_copy": 1 }, { "fieldname": "amount", "fieldtype": "Currency", "label": "Amount", + "no_copy": 1, "read_only": 1 }, { @@ -407,7 +410,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-06-02 06:49:36.493957", + "links": [], + "modified": "2020-02-25 03:09:10.698967", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", From 2a8981929af9d9ad074ed81fb3b7762f8117c473 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 26 Feb 2020 11:36:38 +0530 Subject: [PATCH 17/74] perf: search for customer's return invoices then filter out gl_entries (#20710) --- .../payment_reconciliation.py | 18 ++++++++++++------ .../doctype/sales_invoice/sales_invoice.py | 3 +++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2c04a27b0cd..30804961861 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -92,6 +92,7 @@ class PaymentReconciliation(Document): FROM `tab{doc}`, `tabGL Entry` WHERE (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) + and `tab{doc}`.{party_type_field} = %(party)s and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL and `tabGL Entry`.against_voucher_type = %(voucher_type)s and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s @@ -99,12 +100,17 @@ class PaymentReconciliation(Document): GROUP BY `tab{doc}`.name Having amount > 0 - """.format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), { - 'party': self.party, - 'party_type': self.party_type, - 'voucher_type': voucher_type, - 'account': self.receivable_payable_account - }, as_dict=1) + """.format( + doc=voucher_type, + dr_or_cr=dr_or_cr, + reconciled_dr_or_cr=reconciled_dr_or_cr, + party_type_field=frappe.scrub(self.party_type)), + { + 'party': self.party, + 'party_type': self.party_type, + 'voucher_type': voucher_type, + 'account': self.receivable_payable_account + }, as_dict=1) def add_payment_entries(self, entries): self.set('payments', []) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0b793e847a7..d0fe4f4832a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1523,6 +1523,9 @@ def get_loyalty_programs(customer): else: return lp_details +def on_doctype_update(): + frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"]) + @frappe.whitelist() def create_invoice_discounting(source_name, target_doc=None): invoice = frappe.get_doc("Sales Invoice", source_name) From 6eaa542709d8bae385139655f5ef6f1cef0b883d Mon Sep 17 00:00:00 2001 From: RJPvT <48353029+RJPvT@users.noreply.github.com> Date: Wed, 26 Feb 2020 07:29:06 +0100 Subject: [PATCH 18/74] fix: smaller then instead of bigger then :-( dumb mistake v12 #20693 (#20701) --- erpnext/projects/doctype/task/task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index f2ba80f096f..7cf4150298c 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -212,10 +212,10 @@ def set_multiple_status(names, status): task.save() def set_tasks_as_overdue(): - tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Closed']]}) + tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"]) for task in tasks: - if frappe.db.get_value("Task", task.name, "status") in 'Pending Review': - if getdate(frappe.db.get_value("Task", task.name, "review_date")) < getdate(today()): + if task.status == "Pending Review": + if getdate(task.review_date) > getdate(today()): continue frappe.get_doc("Task", task.name).update_status() From 39861c270c0dd457837755cea480cc4b08704ebc Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Feb 2020 12:51:53 +0530 Subject: [PATCH 19/74] fix: same free item not working for pricing rule (#20713) --- .../doctype/pricing_rule/pricing_rule.py | 2 +- .../doctype/pricing_rule/test_pricing_rule.py | 60 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 9 +-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 1d8d9dbcef5..fc0d8b16f71 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -252,7 +252,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.price_or_product_discount == "Price": apply_price_discount_rule(pricing_rule, item_details, args) else: - get_product_discount_rule(pricing_rule, item_details, doc) + get_product_discount_rule(pricing_rule, item_details, args, doc) item_details.has_pricing_rule = 1 diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 9c1fef69fac..2da71dfd0e0 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -326,6 +326,66 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(item.discount_amount, 110) self.assertEquals(item.rate, 990) + def test_pricing_rule_for_product_discount_on_same_item(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "selling": 1, + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_qty": 0, + "max_qty": 7, + "discount_percentage": 17.5, + "price_or_product_discount": "Product", + "same_item": 1, + "free_qty": 1, + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=1) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + + + def test_pricing_rule_for_product_discount_on_different_item(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "selling": 1, + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_qty": 0, + "max_qty": 7, + "discount_percentage": 17.5, + "price_or_product_discount": "Product", + "same_item": 0, + "free_item": "_Test Item 2", + "free_qty": 1, + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=1) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item 2") + def make_pricing_rule(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index e475563c77c..a2bb2ee927d 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -435,7 +435,7 @@ def apply_pricing_rule_on_transaction(doc): doc.calculate_taxes_and_totals() elif d.price_or_product_discount == 'Product': item_details = frappe._dict({'parenttype': doc.doctype}) - get_product_discount_rule(d, item_details, doc) + get_product_discount_rule(d, item_details, doc=doc) apply_pricing_rule_for_free_items(doc, item_details.free_item_data) doc.set_missing_values() @@ -443,9 +443,10 @@ def get_applied_pricing_rules(item_row): return (item_row.get("pricing_rules").split(',') if item_row.get("pricing_rules") else []) -def get_product_discount_rule(pricing_rule, item_details, doc=None): - free_item = (pricing_rule.free_item - if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code) +def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): + free_item = pricing_rule.free_item + if pricing_rule.same_item: + free_item = item_details.item_code or args.item_code if not free_item: frappe.throw(_("Free item not set in the pricing rule {0}") From 5475b5f562149699498f405c9f40fb36521e3d1e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 26 Feb 2020 14:50:52 +0530 Subject: [PATCH 20/74] fix: validated leave allocation (#20635) * fix: validated leave allocation * fix: changes requested * Update erpnext/hr/doctype/leave_encashment/leave_encashment.py Co-Authored-By: Nabin Hait Co-authored-by: Nabin Hait --- erpnext/hr/doctype/leave_encashment/leave_encashment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 42f0179baf5..ad2cc02fd7c 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -64,6 +64,9 @@ class LeaveEncashment(Document): allocation = self.get_leave_allocation() + if not allocation: + frappe.throw(_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(self.employee, self.leave_type)) + self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\ - get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date) @@ -116,4 +119,4 @@ def create_leave_encashment(leave_allocation): leave_type=allocation.leave_type, encashment_date=allocation.to_date )) - leave_encashment.insert(ignore_permissions=True) \ No newline at end of file + leave_encashment.insert(ignore_permissions=True) From acbc4f648d8ae40c3b6d6d53104399c8aa13cec5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 26 Feb 2020 17:17:06 +0530 Subject: [PATCH 21/74] fix: Mandatory bank account error fix (#20734) * fix: Mandatory bank account error fix * fix: SQL condition --- .../bank_reconciliation/bank_reconciliation.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 52bbe3327a8..2436b15dd4d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -13,9 +13,11 @@ form_grid_templates = { class BankReconciliation(Document): def get_payment_entries(self): - if not (self.bank_account and self.from_date and self.to_date): - msgprint(_("Bank Account, From Date and To Date are Mandatory")) - return + if not (self.from_date and self.to_date): + frappe.throw(_("From Date and To Date are Mandatory")) + + if not self.account: + frappe.throw(_("Account is mandatory to get payment entries")) condition = "" if not self.include_reconciled_entries: @@ -37,6 +39,11 @@ class BankReconciliation(Document): order by t1.posting_date ASC, t1.name DESC """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) + condition = '' + + if self.bank_account: + condition += 'and bank_account = %(bank_account)s' + payment_entries = frappe.db.sql(""" select "Payment Entry" as payment_document, name as payment_entry, @@ -49,10 +56,10 @@ class BankReconciliation(Document): where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1 and posting_date >= %(from)s and posting_date <= %(to)s - and bank_account = %(bank_account)s + {condition} order by posting_date ASC, name DESC - """, {"account": self.account, "from":self.from_date, + """.format(condition=condition), {"account": self.account, "from":self.from_date, "to": self.to_date, "bank_account": self.bank_account}, as_dict=1) pos_entries = [] From 4e3ad9c3f2768656ee3e5095d76de0e8ad8b91e1 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 26 Feb 2020 18:51:35 +0530 Subject: [PATCH 22/74] fix: Lock stock ledger entries that are being reposted. (#20739) - If stock ledger entries are being reposted, don't let any other transaction apply itself on the same. --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2b47a2c09b8..11c61a7f714 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -420,7 +420,7 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition} - order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition), + order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition), tuple([posting_date, posting_time] + values), as_dict=True): future_stock_vouchers.append([d.voucher_type, d.voucher_no]) From 30ad21b7d8b9f91f2533b098cea4c887b80dded6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 26 Feb 2020 18:53:48 +0530 Subject: [PATCH 23/74] fix: sort Issues chronologically (#20740) --- erpnext/support/doctype/issue/issue.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 222554bda13..51b49e8049b 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -363,8 +363,9 @@ ], "icon": "fa fa-ticket", "idx": 7, - "modified": "2019-09-11 09:03:57.465623", - "modified_by": "himanshu@erpnext.com", + "links": [], + "modified": "2020-02-26 02:19:49.477928", + "modified_by": "Administrator", "module": "Support", "name": "Issue", "owner": "Administrator", @@ -384,9 +385,9 @@ "quick_entry": 1, "search_fields": "status,customer,subject,raised_by", "sort_field": "modified", - "sort_order": "ASC", + "sort_order": "DESC", "timeline_field": "customer", "title_field": "subject", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} From 45329232b3d4076f5acfcfa637ae0f15990c51c8 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 27 Feb 2020 12:51:23 +0530 Subject: [PATCH 24/74] fix: only update items if rate or qty changed (#20743) --- erpnext/controllers/accounts_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 001da5b5ea8..5abe7366d0f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1200,6 +1200,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) else: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) + if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + continue if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): frappe.throw(_("Cannot set quantity less than delivered quantity")) From fe337dfb66d709be2c91f8d4f24799e6f3264ba5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 27 Feb 2020 14:27:43 +0530 Subject: [PATCH 25/74] fix: serial no material transfer performance issue (#20722) --- erpnext/stock/doctype/serial_no/serial_no.py | 84 ++++++++++++------- .../stock_ledger_entry.json | 3 +- erpnext/stock/utils.py | 2 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 50f27c18e31..31f84c9aaed 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -205,6 +205,7 @@ def process_serial_no(sle): def validate_serial_no(sle, item_det): serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else [] + validate_material_transfer_entry(sle) if item_det.has_serial_no==0: if serial_nos: @@ -224,7 +225,9 @@ def validate_serial_no(sle, item_det): for serial_no in serial_nos: if frappe.db.exists("Serial No", serial_no): - sr = frappe.get_doc("Serial No", serial_no) + sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order", + "delivery_document_no", "delivery_document_type", "warehouse", + "purchase_document_no", "company"], as_dict=1) if sr.item_code!=sle.item_code: if not allow_serial_nos_with_different_item(serial_no, sle): @@ -305,6 +308,19 @@ def validate_serial_no(sle, item_det): frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}") .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse)) +def validate_material_transfer_entry(sle_doc): + sle_doc.update({ + "skip_update_serial_no": False, + "skip_serial_no_validaiton": False + }) + + if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and + frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"): + if sle_doc.actual_qty < 0: + sle_doc.skip_update_serial_no = True + else: + sle_doc.skip_serial_no_validaiton = True + def validate_so_serial_no(sr, sales_order,): if not sr.sales_order or sr.sales_order!= sales_order: frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can @@ -312,7 +328,8 @@ def validate_so_serial_no(sr, sales_order,): be delivered""").format(sales_order, sr.item_code, sr.name)) def has_duplicate_serial_no(sn, sle): - if sn.warehouse and sle.voucher_type != 'Stock Reconciliation': + if (sn.warehouse and not sle.skip_serial_no_validaiton + and sle.voucher_type != 'Stock Reconciliation'): return True if sn.company != sle.company: @@ -337,7 +354,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): """ allow_serial_nos = False if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0: - stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no) + stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no) if stock_entry.purpose in ("Repack", "Manufacture"): for d in stock_entry.get("items"): if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse): @@ -348,6 +365,7 @@ def allow_serial_nos_with_different_item(sle_serial_no, sle): return allow_serial_nos def update_serial_nos(sle, item_det): + if sle.skip_update_serial_no: return if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \ and item_det.has_serial_no == 1 and item_det.serial_no_series: serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty) @@ -367,22 +385,16 @@ def auto_make_serial_nos(args): serial_nos = get_serial_nos(args.get('serial_no')) created_numbers = [] for serial_no in serial_nos: + is_new = False if frappe.db.exists("Serial No", serial_no): - sr = frappe.get_doc("Serial No", serial_no) - sr.via_stock_ledger = True - sr.item_code = args.get('item_code') - sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None - sr.batch_no = args.get('batch_no') - sr.location = args.get('location') - sr.company = args.get('company') - sr.supplier = args.get('supplier') - if sr.sales_order and args.get('voucher_type') == "Stock Entry" \ - and not args.get('actual_qty', 0) > 0: - sr.sales_order = None - sr.update_serial_no_reference() - sr.save(ignore_permissions=True) + sr = frappe.get_cached_doc("Serial No", serial_no) elif args.get('actual_qty', 0) > 0: - created_numbers.append(make_serial_no(serial_no, args)) + sr = frappe.new_doc("Serial No") + is_new = True + + sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new) + if is_new: + created_numbers.append(sr.name) form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers)) if len(form_links) == 1: @@ -399,20 +411,34 @@ def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') if s.strip()] -def make_serial_no(serial_no, args): - sr = frappe.new_doc("Serial No") - sr.serial_no = serial_no - sr.item_code = args.get('item_code') - sr.company = args.get('company') - sr.batch_no = args.get('batch_no') - sr.via_stock_ledger = args.get('via_stock_ledger') or True - sr.warehouse = args.get('warehouse') +def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False): + serial_no_doc.update({ + "item_code": args.get("item_code"), + "company": args.get("company"), + "batch_no": args.get("batch_no"), + "via_stock_ledger": args.get("via_stock_ledger") or True, + "supplier": args.get("supplier"), + "location": args.get("location"), + "warehouse": (args.get("warehouse") + if args.get("actual_qty", 0) > 0 else None) + }) - sr.validate_item() - sr.update_serial_no_reference(serial_no) - sr.db_insert() + if is_new: + serial_no_doc.serial_no = serial_no - return sr.name + if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry" + and not args.get("actual_qty", 0) > 0): + serial_no_doc.sales_order = None + + serial_no_doc.validate_item() + serial_no_doc.update_serial_no_reference(serial_no) + + if is_new: + serial_no_doc.db_insert() + else: + serial_no_doc.db_update() + + return serial_no_doc def update_serial_nos_after_submit(controller, parentfield): stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index c9eba71b0d0..c03eb79eeca 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -240,6 +240,7 @@ "options": "Company", "print_width": "150px", "read_only": 1, + "search_index": 1, "width": "150px" }, { @@ -274,7 +275,7 @@ "icon": "fa fa-list", "idx": 1, "in_create": 1, - "modified": "2019-11-27 12:17:31.522675", + "modified": "2020-02-25 22:53:33.504681", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 2c6c95393bc..f3381c76091 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -136,7 +136,7 @@ def get_bin(item_code, warehouse): bin_obj.flags.ignore_permissions = 1 bin_obj.insert() else: - bin_obj = frappe.get_doc('Bin', bin) + bin_obj = frappe.get_cached_doc('Bin', bin) bin_obj.flags.ignore_permissions = True return bin_obj From d952e71d970391d0cd7f96ec3a72623aa9ed1f7c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:01:47 +0530 Subject: [PATCH 26/74] fix: Journal Entry not being fetched in Bank Reconciliation --- .../doctype/bank_reconciliation/bank_reconciliation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 2436b15dd4d..883c4207ea9 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = " and (clearance_date is null or clearance_date='0000-00-00')" + condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" journal_entries = frappe.db.sql(""" select @@ -34,11 +34,10 @@ class BankReconciliation(Document): where t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1 and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s - and ifnull(t1.is_opening, 'No') = 'No' %(condition)s + and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """, {"condition":condition, "account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) - + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) condition = '' if self.bank_account: From 5fb66a395302906151a6d6362094bf253d18ad1a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 27 Feb 2020 16:04:01 +0530 Subject: [PATCH 27/74] fix: Remove debug statement --- .../doctype/bank_reconciliation/bank_reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 883c4207ea9..8900767324c 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ class BankReconciliation(Document): condition = "" if not self.include_reconciled_entries: - condition = "and clearance_date IS NULL or clearance_date='0000-00-00'" + condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')" journal_entries = frappe.db.sql(""" select @@ -37,7 +37,7 @@ class BankReconciliation(Document): and ifnull(t1.is_opening, 'No') = 'No' {condition} group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC - """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1, debug=1) + """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) condition = '' if self.bank_account: From f08b5e486627e44bd230c6b484210a609287e167 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 27 Feb 2020 19:07:39 +0530 Subject: [PATCH 28/74] feat: ignore permission when deleting linked emails (#20753) --- erpnext/setup/doctype/company/delete_company_transactions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py index 3052191943c..9979bc51e3a 100644 --- a/erpnext/setup/doctype/company/delete_company_transactions.py +++ b/erpnext/setup/doctype/company/delete_company_transactions.py @@ -106,11 +106,11 @@ def delete_lead_addresses(company_name): frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads))) def delete_communications(doctype, company_name, company_fieldname): - + refrence_docs = frappe.get_all(doctype, filters={company_fieldname:company_name}) reference_doctype_names = [r.name for r in refrence_docs] communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in",reference_doctype_names]}) communication_names = [c.name for c in communications] - frappe.delete_doc("Communication",communication_names) + frappe.delete_doc("Communication", communication_names, ignore_permissions=True) From d17e0b5acab4812fc800b243de0cec09314ac823 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 28 Feb 2020 12:29:11 +0530 Subject: [PATCH 29/74] fix: Item Wise report query fix (#20761) --- .../item_wise_purchase_register.py | 8 ++------ .../item_wise_sales_register.py | 10 +++------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 8b6359c134d..4523f66deb4 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -306,10 +306,6 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Purchase Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -327,8 +323,8 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and - `tabPurchase Invoice`.docstatus = 1 %s %s - """.format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1) + `tabPurchase Invoice`.docstatus = 1 %s + """.format(additional_query_columns) % (conditions), filters, as_dict=1) def get_aii_accounts(): return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany")) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 2cc2db6ca59..786e04dd5aa 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -119,7 +119,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns) data.append(total_row_map.get('total_row')) skip_total_row = 1 - + return columns, data, None, None, None, skip_total_row def get_columns(additional_table_columns, filters): @@ -370,10 +370,6 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - match_conditions = frappe.build_match_conditions("Sales Invoice") - - if match_conditions: - match_conditions = " and {0} ".format(match_conditions) if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) @@ -394,8 +390,8 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent - and `tabSales Invoice`.docstatus = 1 {1} {2} - """.format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec + and `tabSales Invoice`.docstatus = 1 {1} + """.format(additional_query_columns or '', conditions), filters, as_dict=1) #nosec def get_delivery_notes_against_sales_order(item_list): so_dn_map = frappe._dict() From 3fcd575f53c71f9467da43e798037a9b4ce09e8a Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 28 Feb 2020 12:58:41 +0530 Subject: [PATCH 30/74] chore: Rearranged Buying Module Dashboard --- erpnext/config/buying.py | 54 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 6f5ab32b631..1d4054786e6 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -7,6 +7,13 @@ def get_data(): "label": _("Purchasing"), "icon": "fa fa-star", "items": [ + { + "type": "doctype", + "name": "Material Request", + "onboard": 1, + "dependencies": ["Item"], + "description": _("Request for purchase."), + }, { "type": "doctype", "name": "Purchase Order", @@ -20,13 +27,6 @@ def get_data(): "onboard": 1, "dependencies": ["Item", "Supplier"] }, - { - "type": "doctype", - "name": "Material Request", - "onboard": 1, - "dependencies": ["Item"], - "description": _("Request for purchase."), - }, { "type": "doctype", "name": "Request for Quotation", @@ -63,6 +63,11 @@ def get_data(): "name": "Price List", "description": _("Price List master.") }, + { + "type": "doctype", + "name": "Pricing Rule", + "description": _("Rules for applying pricing and discount.") + }, { "type": "doctype", "name": "Product Bundle", @@ -80,11 +85,6 @@ def get_data(): "type": "doctype", "name": "Promotional Scheme", "description": _("Rules for applying different promotional schemes.") - }, - { - "type": "doctype", - "name": "Pricing Rule", - "description": _("Rules for applying pricing and discount.") } ] }, @@ -149,13 +149,6 @@ def get_data(): "reference_doctype": "Purchase Order", "onboard": 1 }, - { - "type": "report", - "is_query_report": True, - "name": "Supplier-Wise Sales Analytics", - "reference_doctype": "Stock Ledger Entry", - "onboard": 1 - }, { "type": "report", "is_query_report": True, @@ -177,6 +170,16 @@ def get_data(): "reference_doctype": "Material Request", "onboard": 1, }, + { + "type": "report", + "is_query_report": True, + "name": "Address And Contacts", + "label": _("Supplier Addresses And Contacts"), + "reference_doctype": "Address", + "route_options": { + "party_type": "Supplier" + } + } ] }, { @@ -226,18 +229,15 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Material Requests for which Supplier Quotations are not created", - "reference_doctype": "Material Request" + "name": "Supplier-Wise Sales Analytics", + "reference_doctype": "Stock Ledger Entry", + "onboard": 1 }, { "type": "report", "is_query_report": True, - "name": "Address And Contacts", - "label": _("Supplier Addresses And Contacts"), - "reference_doctype": "Address", - "route_options": { - "party_type": "Supplier" - } + "name": "Material Requests for which Supplier Quotations are not created", + "reference_doctype": "Material Request" } ] }, From 1041516e8ef43640c5ff48c80f92f346c97a6519 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:00:43 +0530 Subject: [PATCH 31/74] fix: reconciled entries showing in bank reco (#20788) --- .../accounts/doctype/bank_reconciliation/bank_reconciliation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 8900767324c..48fd154a4db 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -38,7 +38,6 @@ class BankReconciliation(Document): group by t2.account, t1.name order by t1.posting_date ASC, t1.name DESC """.format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1) - condition = '' if self.bank_account: condition += 'and bank_account = %(bank_account)s' From 27163e60bbabea83ad85f62bc55af7c015d1eec7 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:03:18 +0530 Subject: [PATCH 32/74] feat: link serial no to batch no (#20779) * feat: link serial no to batch no * fix: test cases --- erpnext/controllers/stock_controller.py | 4 ++++ .../purchase_receipt/test_purchase_receipt.py | 23 +++++++++++++++++++ .../stock/doctype/serial_no/serial_no.json | 6 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 11c61a7f714..2ab1575ee21 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -239,6 +239,10 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue + serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + if serial_nos: + frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) + d.batch_no = None d.db_set("batch_no", None) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index aa09d3bfe5f..7ea84e99131 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -7,6 +7,7 @@ import frappe, erpnext import frappe.defaults from frappe.utils import cint, flt, cstr, today, random_string from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice +from erpnext.stock.doctype.item.test_item import create_item from erpnext import set_perpetual_inventory from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -50,6 +51,28 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250) self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) + + def test_batched_serial_no_purchase(self): + item = frappe.get_doc("Item", { 'item_name': 'Batched Serialized Item' }) + if not item: + item = create_item("Batched Serialized Item") + item.has_batch_no = 1 + item.create_new_batch = 1 + item.has_serial_no = 1 + item.batch_number_series = "BS-BATCH-.##" + item.serial_no_series = "BS-.####" + item.save() + + pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500) + + self.assertTrue(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})) + + pr.load_from_db() + batch_no = pr.items[0].batch_no + pr.cancel() + + self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name})) + self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no})) def test_purchase_receipt_gl_entry(self): pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True) diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 712aadc5254..fb28b5c079d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -6,6 +6,7 @@ "description": "Distinct unit of an Item", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "details", "column_break0", @@ -104,10 +105,11 @@ }, { "fieldname": "batch_no", - "fieldtype": "Data", + "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Batch No", + "options": "Batch", "read_only": 1 }, { @@ -425,7 +427,7 @@ ], "icon": "fa fa-barcode", "idx": 1, - "modified": "2019-08-07 17:28:32.243280", + "modified": "2020-02-28 19:31:09.357323", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", From a6bf96322bb1d6d51e45d895682472c03ab2b4aa Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Mar 2020 15:19:28 +0530 Subject: [PATCH 33/74] chore: control reposting of future gl entries with flags (#20775) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/controllers/stock_controller.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 737c18a8a98..518c4d36af4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -368,7 +368,7 @@ class PurchaseInvoice(BuyingController): update_outstanding_amt(self.credit_to, "Supplier", self.supplier, self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) - if repost_future_gle and cint(self.update_stock) and self.auto_accounting_for_stock: + if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock: from erpnext.controllers.stock_controller import update_gl_entries_after items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d0fe4f4832a..8258833254b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -717,7 +717,7 @@ class SalesInvoice(SellingController): update_outstanding_amt(self.debit_to, "Customer", self.customer, self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name) - if repost_future_gle and cint(self.update_stock) \ + if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \ and cint(auto_accounting_for_stock): items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ab1575ee21..d13edc34573 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -34,7 +34,7 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) - if repost_future_gle: + if (repost_future_gle or self.flags.repost_future_gle): items, warehouses = self.get_items_and_warehouses() update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account, company=self.company) From 94200b1179dd6d77f2714b697b7749f0b8e325ea Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 2 Mar 2020 15:51:22 +0530 Subject: [PATCH 34/74] chore: Item Price and Product Bundle Form cleanup (#20773) * chore: Item Price and Product Bundle Form cleanup * fix: Trailing comma --- .../accounts/doctype/pricing_rule/utils.py | 6 +- .../product_bundle_item.json | 247 +++++------------- .../stock/doctype/item_price/item_price.json | 11 +- .../doctype/item_price/test_records.json | 13 +- 4 files changed, 75 insertions(+), 202 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index a2bb2ee927d..2ffa27f6d99 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -245,7 +245,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None): def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type): fieldname, msg = '', '' - type_of_transaction = 'purcahse' if transaction_type == "buying" else "sale" + type_of_transaction = 'purchase' if transaction_type == 'buying' else 'sale' for field, value in {'min_qty': qty, 'min_amt': amount}.items(): if (args.get(field) and value < args.get(field) @@ -465,7 +465,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): item_details.free_item_data.update(item_data) item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom - item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, + item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, item_details.free_item_data['uom']).get("conversion_factor", 1) if item_details.get("parenttype") == 'Purchase Order': @@ -508,7 +508,7 @@ def validate_coupon_code(coupon_name): frappe.throw(_("Sorry,coupon code validity has not started")) elif coupon.valid_upto: if coupon.valid_upto < getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has expired")) + frappe.throw(_("Sorry,coupon code validity has expired")) elif coupon.used>=coupon.maximum_use: frappe.throw(_("Sorry,coupon code are exhausted")) else: diff --git a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json index 38f51dead49..dc071e4d65e 100644 --- a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json +++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json @@ -1,189 +1,76 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-05-23 16:55:51", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2013-05-23 16:55:51", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "qty", + "description", + "rate", + "uom" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_code", - "oldfieldtype": "Link", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "label": "Item", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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": "Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty", + "oldfieldname": "qty", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rate", - "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": "Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "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 - }, + "fieldname": "rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "print_hide": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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": "UOM", - "length": 0, - "no_copy": 0, - "oldfieldname": "uom", - "oldfieldtype": "Link", - "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, - "unique": 0 + "fieldname": "uom", + "fieldtype": "Link", + "in_list_view": 1, + "label": "UOM", + "oldfieldname": "uom", + "oldfieldtype": "Link", + "options": "UOM", + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-02-20 13:24:05.633546", - "modified_by": "Administrator", - "module": "Selling", - "name": "Product Bundle Item", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 1, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2020-02-28 14:06:05.725655", + "modified_by": "Administrator", + "module": "Selling", + "name": "Product Bundle Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 8456b584ca0..2e0ddfdaefc 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -10,7 +10,6 @@ "item_code", "uom", "packing_unit", - "min_qty", "column_break_17", "item_name", "brand", @@ -63,13 +62,6 @@ "fieldtype": "Int", "label": "Packing Unit" }, - { - "default": "1", - "fieldname": "min_qty", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Minimum Qty " - }, { "fieldname": "column_break_17", "fieldtype": "Column Break" @@ -216,7 +208,7 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2019-12-31 03:11:09.702250", + "modified": "2020-02-28 14:21:25.580331", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", @@ -251,6 +243,7 @@ } ], "quick_entry": 1, + "sort_field": "modified", "sort_order": "ASC", "title_field": "item_name", "track_changes": 1 diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json index 473bacb3c33..0a3d7e81985 100644 --- a/erpnext/stock/doctype/item_price/test_records.json +++ b/erpnext/stock/doctype/item_price/test_records.json @@ -4,7 +4,6 @@ "item_code": "_Test Item", "price_list": "_Test Price List", "price_list_rate": 100, - "min_qty": 2, "valid_from": "2017-04-18", "valid_upto": "2017-04-26" }, @@ -12,8 +11,7 @@ "doctype": "Item Price", "item_code": "_Test Item", "price_list": "_Test Price List Rest of the World", - "price_list_rate": 10, - "min_qty": 5 + "price_list_rate": 10 }, { "doctype": "Item Price", @@ -22,7 +20,6 @@ "price_list_rate": 20, "valid_from": "2017-04-18", "valid_upto": "2017-04-26", - "min_qty": 7, "customer": "_Test Customer", "uom": "_Test UOM" }, @@ -31,19 +28,15 @@ "item_code": "_Test Item Home Desktop 100", "price_list": "_Test Price List", "price_list_rate": 1000, - "min_qty" : 10, "valid_from": "2017-04-10", - "valid_upto": "2017-04-17", - "min_qty": 2 + "valid_upto": "2017-04-17" }, { "doctype": "Item Price", "item_code": "_Test Item Home Desktop Manufactured", "price_list": "_Test Price List", "price_list_rate": 1000, - "min_qty" : 10, "valid_from": "2017-04-10", - "valid_upto": "2017-04-17", - "min_qty": 2 + "valid_upto": "2017-04-17" } ] From acd93b3bd142364661b2065af8cde00c53136a47 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 2 Mar 2020 18:05:59 +0530 Subject: [PATCH 35/74] fix: use system language to translate strings (#20792) --- .../connectors/woocommerce_connection.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 28c2ab9e545..4422d23e380 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -47,11 +47,12 @@ def _order(*args, **kwargs): return "success" if event == "created": + sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") link_customer_and_address(raw_billing_data, customer_name) - link_items(order.get("line_items"), woocommerce_settings) - create_sales_order(order, woocommerce_settings, customer_name) + link_items(order.get("line_items"), woocommerce_settings, sys_lang) + create_sales_order(order, woocommerce_settings, customer_name, sys_lang) def link_customer_and_address(raw_billing_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") @@ -100,7 +101,7 @@ def link_customer_and_address(raw_billing_data, customer_name): frappe.rename_doc("Address", old_address_title, new_address_title) -def link_items(items_list, woocommerce_settings): +def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: item_woo_com_id = item_data.get("product_id") @@ -112,14 +113,14 @@ def link_items(items_list, woocommerce_settings): item = frappe.new_doc("Item") item.item_name = item_data.get("name") - item.item_code = _("woocommerce - {0}").format(item_data.get("product_id")) + item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") - item.item_group = _("WooCommerce Products") - item.stock_uom = woocommerce_settings.uom or _("Nos") + item.item_group = _("WooCommerce Products", sys_lang) + item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang) item.flags.ignore_mandatory = True item.save() -def create_sales_order(order, woocommerce_settings, customer_name): +def create_sales_order(order, woocommerce_settings, customer_name, sys_lang): new_sales_order = frappe.new_doc("Sales Order") new_sales_order.customer = customer_name @@ -133,14 +134,14 @@ def create_sales_order(order, woocommerce_settings, customer_name): new_sales_order.company = woocommerce_settings.company - set_items_in_sales_order(new_sales_order, woocommerce_settings, order) + set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang) new_sales_order.flags.ignore_mandatory = True new_sales_order.insert() new_sales_order.submit() frappe.db.commit() -def set_items_in_sales_order(new_sales_order, woocommerce_settings, order): +def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang): company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') for item in order.get("line_items"): @@ -154,10 +155,10 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order): "item_name": found_item.item_name, "description": found_item.item_name, "delivery_date": new_sales_order.delivery_date, - "uom": woocommerce_settings.uom or _("Nos"), + "uom": woocommerce_settings.uom or _("Nos", sys_lang), "qty": item.get("quantity"), "rate": item.get("price"), - "warehouse": woocommerce_settings.warehouse or _("Stores - {0}").format(company_abbr) + "warehouse": woocommerce_settings.warehouse or _("Stores - {0}", sys_lang).format(company_abbr) }) add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account) From 6a734ecd34e45a3c096d5e590ad1d33c441ba400 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 2 Mar 2020 18:06:21 +0530 Subject: [PATCH 36/74] fix: use get all instead of get list to fetch item attributes (#20793) --- erpnext/portal/product_configurator/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index ce507a39486..327a128068f 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -188,7 +188,9 @@ def get_attributes_and_values(item_code): def get_numeric_values(): attribute_values_list = [] - numeric_attributes = frappe.get_list("Item Attribute", fields=['name', 'from_range', 'to_range', 'increment'], filters={"numeric_values": 1}) + numeric_attributes = frappe.db.get_all("Item Attribute", + fields=['name', 'from_range', 'to_range', 'increment'], + filters={"numeric_values": 1}) for attribute in numeric_attributes: from_range = attribute["from_range"] to_range = attribute['to_range'] + attribute['increment'] From d7839eb8acde1f0238aa8db74adda442eaaa369f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:09:24 +0530 Subject: [PATCH 37/74] fix: Total amount not displayed in Journal Entry (#20795) * fix: Total amount not displayed in Journal Entry * fix: Update paid_to_received field * fix: set total amount Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 428b9acd150..04c5c9ec679 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -459,8 +459,7 @@ class JournalEntry(AccountsController): for d in self.get('accounts'): if d.party_type in ['Customer', 'Supplier'] and d.party: if not pay_to_recd_from: - pay_to_recd_from = frappe.db.get_value(d.party_type, d.party, - "customer_name" if d.party_type=="Customer" else "supplier_name") + pay_to_recd_from = d.party if pay_to_recd_from and pay_to_recd_from == d.party: party_amount += (d.debit_in_account_currency or d.credit_in_account_currency) @@ -471,7 +470,8 @@ class JournalEntry(AccountsController): bank_account_currency = d.account_currency if pay_to_recd_from: - self.pay_to_recd_from = pay_to_recd_from + self.pay_to_recd_from = frappe.db.get_value(d.party_type, pay_to_recd_from, + "customer_name" if d.party_type=="Customer" else "supplier_name") if bank_amount: total_amount = bank_amount currency = bank_account_currency From 7766530afcd30641de2d7497e5a1465f407ea2e1 Mon Sep 17 00:00:00 2001 From: Priyanka Gangar <59438065+0925-pinka@users.noreply.github.com> Date: Mon, 2 Mar 2020 19:24:14 +0530 Subject: [PATCH 38/74] fix: fetch sales person name (#20801) * fix: fetch sales person name * Update sales_person.js Co-authored-by: pinka0925 <44537026+pinka0925@users.noreply.github.com> Co-authored-by: Nabin Hait --- erpnext/setup/doctype/sales_person/sales_person.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 9ff37fa4e83..89ca4a9dd74 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,6 +19,11 @@ frappe.ui.form.on('Sales Person', { } } }; + + frm.make_methods = { + 'Sales Order': () => frappe.new_doc("Sales Order") + .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) + } } }); From 2728590471d850dab7df1ef0e76655b4de428881 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 4 Mar 2020 10:23:49 +0530 Subject: [PATCH 39/74] fix: HSN code no visible in GST itemised sales register --- .../gst_itemised_sales_register/gst_itemised_sales_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py index ab523e74e67..e13f509f475 100644 --- a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py +++ b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py @@ -15,7 +15,7 @@ def execute(filters=None): dict(fieldtype='Data', label='GST Category', fieldname="gst_category", width=120), dict(fieldtype='Data', label='Export Type', fieldname="export_type", width=120), dict(fieldtype='Data', label='E-Commerce GSTIN', fieldname="ecommerce_gstin", width=130), - dict(fieldtype='Data', label='HSN Code', fieldname="hsn_code", width=120) + dict(fieldtype='Data', label='HSN Code', fieldname="gst_hsn_code", width=120) ], additional_query_columns=[ 'customer_gstin', 'billing_address_gstin', From 12cdf0fb5242a3a7de10775174d66dfa0aa8a6ad Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 5 Mar 2020 10:08:43 +0530 Subject: [PATCH 40/74] fix: use ERPNext in welcome email when default company is not set Signed-off-by: Chinmay D. Pai --- erpnext/setup/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index d1c206d8b1d..ffd5ab1e840 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -141,6 +141,6 @@ def insert_record(records): raise def welcome_email(): - site_name = get_default_company() - title = _("Welcome to {0}".format(site_name)) + site_name = get_default_company() or "ERPNext" + title = _("Welcome to {0}").format(site_name) return title From 1f80c8dffef3d779a32fa3e9eb12b5517cb167e7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 5 Mar 2020 13:04:53 +0530 Subject: [PATCH 41/74] perf: improve gl entry submission (#20802) * perf: improve gl entry submission * perf: add indexes * fix: replace **kwargs with *args * fix: syntax error * fix: travis * chore: remove purchase invoice from status updater * fix: set_staus args Co-Authored-By: Nabin Hait * fix: only update status for invoices & fees * [bug][fix]: set status to object instead of variable (#20790) Co-authored-by: Nabin Hait Co-authored-by: Saurabh --- .../discounted_invoice.json | 5 +- .../discounted_invoice/discounted_invoice.py | 2 +- erpnext/accounts/doctype/gl_entry/gl_entry.py | 30 +++- .../payment_entry/test_payment_entry.py | 45 +++++- .../purchase_invoice/purchase_invoice.py | 49 +++++++ .../doctype/sales_invoice/sales_invoice.py | 128 +++++++++++------- erpnext/accounts/general_ledger.py | 5 +- erpnext/controllers/status_updater.py | 11 -- 8 files changed, 212 insertions(+), 63 deletions(-) diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 5c3519a1592..02b0c4d937b 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -18,7 +18,8 @@ "in_list_view": 1, "label": "Invoice", "options": "Sales Invoice", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fetch_from": "sales_invoice.customer", @@ -60,7 +61,7 @@ } ], "istable": 1, - "modified": "2019-09-26 11:05:36.016772", + "modified": "2020-02-20 16:16:20.724620", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py index 93dfcc14bda..109737f7276 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py @@ -7,4 +7,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class DiscountedInvoice(Document): - pass + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 041e419752b..f9e4fd77148 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -232,11 +232,36 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga if bal < 0 and not on_cancel: frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) - # Update outstanding amt on against voucher if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: + update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal) + +def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal): + data = [] + # Update outstanding amt on against voucher + if against_voucher_type == "Fees": ref_doc = frappe.get_doc(against_voucher_type, against_voucher) ref_doc.db_set('outstanding_amount', bal) ref_doc.set_status(update=True) + return + elif against_voucher_type == "Purchase Invoice": + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as purchase_invoice", "outstanding_amount", + "is_return", "due_date", "docstatus"]) + elif against_voucher_type == "Sales Invoice": + from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status + data = frappe.db.get_value(against_voucher_type, against_voucher, + ["name as sales_invoice", "outstanding_amount", "is_discounted", + "is_return", "due_date", "docstatus"]) + + precision = frappe.get_precision(against_voucher_type, "outstanding_amount") + data = list(data) + data.append(precision) + status = get_status(data) + frappe.db.set_value(against_voucher_type, against_voucher, { + 'outstanding_amount': bal, + 'status': status + }) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") @@ -274,6 +299,9 @@ def update_against_account(voucher_type, voucher_no): if d.against != new_against: frappe.db.set_value("GL Entry", d.name, "against", new_against) +def on_doctype_update(): + frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"]) + frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) def rename_gle_sle_docs(): for doctype in ["GL Entry", "Stock Ledger Entry"]: diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index a7ab1754bfc..5303743d424 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -149,6 +149,49 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) + + def test_payment_against_sales_invoice_to_check_status(self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50) + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.target_exchange_rate = 50 + pe.insert() + pe.submit() + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + self.assertEqual(si.status, 'Paid') + + pe.cancel() + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 100) + self.assertEqual(si.status, 'Unpaid') + + def test_payment_against_purchase_invoice_to_check_status(self): + pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", + currency="USD", conversion_rate=50) + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + pe.source_exchange_rate = 50 + pe.insert() + pe.submit() + + outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + self.assertEqual(pi.status, 'Paid') + + pe.cancel() + + outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 100) + self.assertEqual(pi.status, 'Unpaid') + def test_payment_entry_against_ec(self): payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account') @@ -566,4 +609,4 @@ class TestPaymentEntry(unittest.TestCase): self.assertEqual(expected_party_account_balance, party_account_balance) accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + accounts_settings.save() \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 518c4d36af4..754a97558b3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -125,6 +125,27 @@ class PurchaseInvoice(BuyingController): else: self.remarks = _("No Remarks") + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + if not status: + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_return, + self.due_date, + self.docstatus, + precision + ] + self.status = get_status(args) + + if update: + self.db_set('status', self.status, update_modified = update_modified) + def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) @@ -964,6 +985,34 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() +def get_status(*args): + purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0] + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to debit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}): + status = "Debit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8258833254b..ed34127fbb3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1211,63 +1211,84 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) - def get_discounting_status(self): - status = None - if self.is_discounted: - invoice_discounting_list = frappe.db.sql(""" - select status - from `tabInvoice Discounting` id, `tabDiscounted Invoice` d - where - id.name = d.parent - and d.sales_invoice=%s - and id.docstatus=1 - and status in ('Disbursed', 'Settled') - """, self.name) - for d in invoice_discounting_list: - status = d[0] - if status == "Disbursed": - break - return status - def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): self.status = 'Draft' return - precision = self.precision("outstanding_amount") - outstanding_amount = flt(self.outstanding_amount, precision) - due_date = getdate(self.due_date) - nowdate = getdate() - discountng_status = self.get_discounting_status() - if not status: - if self.docstatus == 2: - status = "Cancelled" - elif self.docstatus == 1: - if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Overdue and Discounted" - elif outstanding_amount > 0 and due_date < nowdate: - self.status = "Overdue" - elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': - self.status = "Unpaid and Discounted" - elif outstanding_amount > 0 and due_date >= nowdate: - self.status = "Unpaid" - #Check if outstanding amount is 0 due to credit note issued against invoice - elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): - self.status = "Credit Note Issued" - elif self.is_return == 1: - self.status = "Return" - elif outstanding_amount<=0: - self.status = "Paid" - else: - self.status = "Submitted" - else: - self.status = "Draft" + precision = self.precision("outstanding_amount") + args = [ + self.name, + self.outstanding_amount, + self.is_discounted, + self.is_return, + self.due_date, + self.docstatus, + precision, + ] + self.status = get_status(args) if update: self.db_set('status', self.status, update_modified = update_modified) +def get_discounting_status(sales_invoice): + status = None + + invoice_discounting_list = frappe.db.sql(""" + select status + from `tabInvoice Discounting` id, `tabDiscounted Invoice` d + where + id.name = d.parent + and d.sales_invoice=%s + and id.docstatus=1 + and status in ('Disbursed', 'Settled') + """, sales_invoice) + + for d in invoice_discounting_list: + status = d[0] + if status == "Disbursed": + break + + return status + +def get_status(*args): + sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0] + + discounting_status = None + if is_discounted: + discounting_status = get_discounting_status(sales_invoice) + + outstanding_amount = flt(outstanding_amount, precision) + due_date = getdate(due_date) + now_date = getdate() + + if docstatus == 2: + status = "Cancelled" + elif docstatus == 1: + if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed': + status = "Overdue and Discounted" + elif outstanding_amount > 0 and due_date < now_date: + status = "Overdue" + elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed': + status = "Unpaid and Discounted" + elif outstanding_amount > 0 and due_date >= now_date: + status = "Unpaid" + #Check if outstanding amount is 0 due to credit note issued against invoice + elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}): + status = "Credit Note Issued" + elif is_return == 1: + status = "Return" + elif outstanding_amount <=0: + status = "Paid" + else: + status = "Submitted" + else: + status = "Draft" + + return status + def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return @@ -1435,6 +1456,21 @@ def get_inter_company_details(doc, doctype): "company": company } +def get_internal_party(parties, link_doctype, doc): + if len(parties) == 1: + party = parties[0].name + else: + # If more than one Internal Supplier/Customer, get supplier/customer on basis of address + if doc.get('company_address') or doc.get('shipping_address'): + party = frappe.db.get_value("Dynamic Link", {"parent": doc.get('company_address') or doc.get('shipping_address'), + "parenttype": "Address", "link_doctype": link_doctype}, "link_name") + + if not party: + party = parties[0].name + else: + party = parties[0].name + + return party def validate_inter_company_transaction(doc, doctype): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index bb1b7e392dc..6d53530321f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -140,8 +140,11 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): gle = frappe.get_doc(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost - gle.insert() + gle.validate() + gle.flags.ignore_permissions = True + gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) + gle.flags.ignore_validate = True gle.submit() def validate_account_for_perpetual_inventory(gl_map): diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 8b275a64fb3..b465a106f0e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -44,17 +44,6 @@ status_map = { ["Closed", "eval:self.status=='Closed'"], ["On Hold", "eval:self.status=='On Hold'"], ], - "Purchase Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", - "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Purchase Order": [ ["Draft", None], ["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"], From 69d3c4ff254aeeb5f9279220baf6443b447af272 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 6 Mar 2020 10:57:43 +0530 Subject: [PATCH 42/74] fix: update_child_qty_rate() missing argument (#20839) --- erpnext/public/js/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 3f444f83879..98986f8bf1b 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -439,12 +439,13 @@ erpnext.utils.update_child_items = function(opts) { const dialog = new frappe.ui.Dialog({ title: __("Update Items"), fields: [ - {fieldtype:'Section Break', label: __('Items')}, { fieldname: "trans_items", fieldtype: "Table", + label: "Items", cannot_add_rows: cannot_add_row, in_place_edit: true, + reqd: 1, data: this.data, get_data: () => { return this.data; From 6c4cddccf6c9bec81c533aaf72865998e42de72d Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Mar 2020 11:06:41 +0530 Subject: [PATCH 43/74] fix: show uom in print formats instead of stock uom (#20844) Co-authored-by: Saqib Ansari --- .../purchase_invoice_item/purchase_invoice_item.json | 3 +-- .../selling/doctype/quotation_item/quotation_item.json | 8 ++++++-- .../doctype/sales_order_item/sales_order_item.json | 4 ++-- .../doctype/delivery_note_item/delivery_note_item.json | 6 ++++-- .../purchase_receipt_item/purchase_receipt_item.json | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index acb0398b5c0..36bb2be3dc5 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -198,7 +198,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -771,7 +770,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2019-12-04 12:23:17.046413", + "modified": "2020-03-05 14:20:17.297284", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index a299b8809ab..3ff5555e827 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-03-07 11:42:57", "doctype": "DocType", "document_type": "Document", @@ -163,6 +164,7 @@ "oldfieldname": "stock_uom", "oldfieldtype": "Data", "options": "UOM", + "print_hide": 1, "print_width": "100px", "read_only": 1, "width": "100px" @@ -176,7 +178,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -382,6 +383,7 @@ "read_only": 1 }, { + "default": "0", "fieldname": "is_free_item", "fieldtype": "Check", "label": "Is Free Item", @@ -517,6 +519,7 @@ }, { "allow_on_submit": 1, + "default": "0", "fieldname": "page_break", "fieldtype": "Check", "label": "Page Break", @@ -574,7 +577,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-05-01 17:39:14.228141", + "links": [], + "modified": "2020-03-05 14:18:58.783751", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 317f284eff3..c95f236ebba 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -209,7 +209,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -758,7 +757,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-12-11 18:06:26.238169", + "links": [], + "modified": "2020-03-05 14:20:28.085117", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index af06daeabd3..782ac84e57d 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-04-22 13:15:44", "doctype": "DocType", @@ -180,6 +181,7 @@ "oldfieldname": "stock_uom", "oldfieldtype": "Data", "options": "UOM", + "print_hide": 1, "print_width": "50px", "read_only": 1, "reqd": 1, @@ -195,7 +197,6 @@ "in_list_view": 1, "label": "UOM", "options": "UOM", - "print_hide": 1, "reqd": 1 }, { @@ -702,7 +703,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-05-25 22:08:27.452734", + "links": [], + "modified": "2020-03-05 14:18:33.131672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 16ec8db335c..d5cf4cadb14 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -222,7 +222,6 @@ "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", - "print_hide": 1, "print_width": "100px", "reqd": 1, "width": "100px" @@ -823,7 +822,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-14 16:03:25.499557", + "links": [], + "modified": "2020-03-05 14:19:48.799370", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 875e0f5fb23589f2e44ffbf344a1f01cf37d1f7b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Mar 2020 08:06:07 +0530 Subject: [PATCH 44/74] fix: Acccounting Dimension disappearing in first row after refresh --- erpnext/public/js/utils/dimension_tree_filter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 7abe4a7aab1..8b6de0f5179 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -35,11 +35,13 @@ erpnext.doctypes_with_dimensions.forEach((doctype) => { } } - if (frm.doc.items && frm.doc.items.length) { + if (frm.doc.items && frm.doc.items.length && frm.doc.docstatus === 0 + && (!frm.doc.items[0][dimension['fieldname']])) { frm.doc.items[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; } - if (frm.doc.accounts && frm.doc.accounts.length) { + if (frm.doc.accounts && frm.doc.accounts.length && frm.doc.docstatus === 0 + && (!frm.doc.items[0][dimension['fieldname']])) { frm.doc.accounts[0][dimension['fieldname']] = erpnext.default_dimensions[frm.doc.company][dimension['document_type']]; } } From 2f2cef6c5f8574b5e928280436aec0fb80b13588 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Mar 2020 18:07:55 +0530 Subject: [PATCH 45/74] fix: ModuleNotFoundError on Turkey Company setup (#20854) --- erpnext/regional/turkey/setup.py | 4 ++++ erpnext/setup/doctype/company/company.py | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 erpnext/regional/turkey/setup.py diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py new file mode 100644 index 00000000000..ebf3b2bee1f --- /dev/null +++ b/erpnext/regional/turkey/setup.py @@ -0,0 +1,4 @@ +from __future__ import unicode_literals + +def setup(company=None, patch=True): + pass \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index ff3515485c4..c3fd3566578 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -416,8 +416,13 @@ def install_country_fixtures(company): company_doc = frappe.get_doc("Company", company) path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country)) if os.path.exists(path.encode("utf-8")): - frappe.get_attr("erpnext.regional.{0}.setup.setup" - .format(frappe.scrub(company_doc.country)))(company_doc, False) + try: + module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country)) + frappe.get_attr(module_name)(company_doc, False) + except Exception as e: + frappe.log_error(str(e), frappe.get_traceback()) + frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country))) + def update_company_current_month_sales(company): current_month_year = formatdate(today(), "MM-yyyy") From d08cff9efc35892980496b11a913a1ab836e64e1 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Mar 2020 18:13:38 +0530 Subject: [PATCH 46/74] fix: Validate Serial No/Batch No against unserialized Item in Stock Reconciliation (#20859) --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index ca2741ccfba..afa239466b5 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -190,6 +190,9 @@ class StockReconciliation(StockController): has_serial_no = True self.get_sle_for_serialized_items(row, sl_entries) else: + if row.serial_no or row.batch_no: + frappe.throw(_("Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.") \ + .format(row.idx, frappe.bold(row.item_code))) previous_sle = get_previous_sle({ "item_code": row.item_code, "warehouse": row.warehouse, From b607963fba27e92224ce8a40b32443d33717643f Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 9 Mar 2020 18:30:42 +0530 Subject: [PATCH 47/74] fix: [pos] customer group filter resets on syncing offline invoices (#20874) --- erpnext/accounts/doctype/sales_invoice/pos.py | 9 ++++++--- erpnext/accounts/page/pos/pos.js | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 749816f7814..c0e128567f9 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -207,7 +207,7 @@ def get_customers_list(pos_profile={}): if pos_profile.get('customer_groups'): # Get customers based on the customer groups defined in the POS profile for d in pos_profile.get('customer_groups'): - customer_groups.extend([d.name for d in get_child_nodes('Customer Group', d.customer_group)]) + customer_groups.extend([d.get('name') for d in get_child_nodes('Customer Group', d.get('customer_group'))]) cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups))) return frappe.db.sql(""" select name, customer_name, customer_group, @@ -387,7 +387,9 @@ def get_pricing_rule_data(doc): @frappe.whitelist() -def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): +def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={}): + import json + if isinstance(doc_list, string_types): doc_list = json.loads(doc_list) @@ -421,7 +423,8 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): name_list.append(name) email_queue = make_email_queue(email_queue_list) - customers = get_customers_list() + pos_profile = json.loads(pos_profile) + customers = get_customers_list(pos_profile) return { 'invoice': name_list, 'email_queue': email_queue, diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 8dc00f3f218..24fcb41a5db 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1769,6 +1769,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", freeze: true, args: { + pos_profile: me.pos_profile_data, doc_list: me.si_docs, email_queue_list: me.email_queue_list, customers_list: me.customers_list From 0317576434c948ebbeb03018e7d56b843dfcd6fd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Mar 2020 22:38:41 +0530 Subject: [PATCH 48/74] fix: HSN code not visible in GST Itemised Purchase register --- .../item_wise_purchase_register.py | 4 ++-- .../gst_itemised_purchase_register.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 4523f66deb4..11f4e0d4e85 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -54,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, - 'customer': d.supplier, - 'customer_name': d.supplier_name + 'supplier': d.supplier, + 'supplier_name': d.supplier_name } if additional_query_columns: diff --git a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py index 3ce2547b20f..b5948f9952d 100644 --- a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py +++ b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py @@ -13,9 +13,9 @@ def execute(filters=None): dict(fieldtype='Data', label='GST Category', fieldname="gst_category", width=120), dict(fieldtype='Data', label='Export Type', fieldname="export_type", width=120), dict(fieldtype='Data', label='E-Commerce GSTIN', fieldname="ecommerce_gstin", width=130), - dict(fieldtype='Data', label='HSN Code', fieldname="hsn_code", width=120), - dict(fieldtype='Data', label='Supplier Invoice No', fieldname="supplier_invoice_no", width=120), - dict(fieldtype='Date', label='Supplier Invoice Date', fieldname="supplier_invoice_date", width=100) + dict(fieldtype='Data', label='HSN Code', fieldname="gst_hsn_code", width=120), + dict(fieldtype='Data', label='Supplier Invoice No', fieldname="bill_no", width=120), + dict(fieldtype='Date', label='Supplier Invoice Date', fieldname="bill_date", width=100) ], additional_query_columns=[ 'supplier_gstin', 'company_gstin', From ee003495f6470c88fc70192e60def6de07370fed Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Mar 2020 10:36:05 +0530 Subject: [PATCH 49/74] fix: Get invoice details only if invoice is selected --- erpnext/accounts/doctype/c_form/c_form.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/c_form/c_form.js b/erpnext/accounts/doctype/c_form/c_form.js index 3d0fc0a42ce..846408ff244 100644 --- a/erpnext/accounts/doctype/c_form/c_form.js +++ b/erpnext/accounts/doctype/c_form/c_form.js @@ -32,10 +32,12 @@ frappe.ui.form.on('C-Form Invoice Detail', { invoice_no(frm, cdt, cdn) { let d = frappe.get_doc(cdt, cdn); - frm.call('get_invoice_details', { - invoice_no: d.invoice_no - }).then(r => { - frappe.model.set_value(cdt, cdn, r.message); - }); + if (d.invoice_no) { + frm.call('get_invoice_details', { + invoice_no: d.invoice_no + }).then(r => { + frappe.model.set_value(cdt, cdn, r.message); + }); + } } }); From 97dfe2ac32aa38b0578476b84c278e74908138aa Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 16 Mar 2020 13:47:47 +0530 Subject: [PATCH 50/74] fix: local variable 'benefit_amount' referenced before assignment (#20948) --- .../employee_benefit_application/employee_benefit_application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py index fea4246d646..feaa92590a3 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py @@ -190,6 +190,7 @@ def get_benefit_component_amount(employee, start_date, end_date, salary_componen component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component", salary_component, ["max_benefit_amount", "depends_on_payment_days"]) + benefit_amount = 0 if benefit_application: benefit_amount = frappe.db.get_value("Employee Benefit Application Detail", {"parent": benefit_application[0][0], "earning_component": salary_component}, "amount") From e2a31f221a0c0977bab5623d3b574eeee402bbfc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 16 Mar 2020 15:36:10 +0530 Subject: [PATCH 51/74] fix: Multiple fixes for travis (#20950) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 32 ++------ .../payment_entry/test_payment_entry.py | 24 +++--- .../purchase_invoice/purchase_invoice.py | 52 +------------ erpnext/accounts/doctype/sales_invoice/pos.py | 5 +- .../doctype/sales_invoice/sales_invoice.py | 78 ++++++++----------- .../sales_invoice/test_sales_invoice.py | 10 +-- erpnext/accounts/general_ledger.py | 5 +- erpnext/controllers/status_updater.py | 11 +++ erpnext/setup/doctype/company/test_company.py | 2 +- erpnext/stock/doctype/item/item.py | 11 ++- .../purchase_receipt/test_purchase_receipt.py | 10 ++- 11 files changed, 89 insertions(+), 151 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index f9e4fd77148..512e348c8bf 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -233,35 +233,13 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: - update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal) - -def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal): - data = [] - # Update outstanding amt on against voucher - if against_voucher_type == "Fees": ref_doc = frappe.get_doc(against_voucher_type, against_voucher) - ref_doc.db_set('outstanding_amount', bal) - ref_doc.set_status(update=True) - return - elif against_voucher_type == "Purchase Invoice": - from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status - data = frappe.db.get_value(against_voucher_type, against_voucher, - ["name as purchase_invoice", "outstanding_amount", - "is_return", "due_date", "docstatus"]) - elif against_voucher_type == "Sales Invoice": - from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status - data = frappe.db.get_value(against_voucher_type, against_voucher, - ["name as sales_invoice", "outstanding_amount", "is_discounted", - "is_return", "due_date", "docstatus"]) - precision = frappe.get_precision(against_voucher_type, "outstanding_amount") - data = list(data) - data.append(precision) - status = get_status(data) - frappe.db.set_value(against_voucher_type, against_voucher, { - 'outstanding_amount': bal, - 'status': status - }) + # Didn't use db_set for optimisation purpose + ref_doc.outstanding_amount = bal + frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal) + + ref_doc.set_status(update=True) def validate_frozen_account(account, adv_adj=None): frozen_account = frappe.db.get_value("Account", account, "freeze_account") diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5303743d424..a25e0e32c86 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -161,15 +161,15 @@ class TestPaymentEntry(unittest.TestCase): pe.insert() pe.submit() - outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 0) - self.assertEqual(si.status, 'Paid') + outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 0) + self.assertEqual(status, 'Paid') pe.cancel() - outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 100) - self.assertEqual(si.status, 'Unpaid') + outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 100) + self.assertEqual(status, 'Unpaid') def test_payment_against_purchase_invoice_to_check_status(self): pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", @@ -182,15 +182,15 @@ class TestPaymentEntry(unittest.TestCase): pe.insert() pe.submit() - outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 0) - self.assertEqual(pi.status, 'Paid') + outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 0) + self.assertEqual(status, 'Paid') pe.cancel() - outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", pi.name, "outstanding_amount")) - self.assertEqual(outstanding_amount, 100) - self.assertEqual(pi.status, 'Unpaid') + outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"]) + self.assertEqual(flt(outstanding_amount), 250) + self.assertEqual(status, 'Unpaid') def test_payment_entry_against_ec(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 754a97558b3..698c2ab333d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -125,27 +125,6 @@ class PurchaseInvoice(BuyingController): else: self.remarks = _("No Remarks") - def set_status(self, update=False, status=None, update_modified=True): - if self.is_new(): - if self.get('amended_from'): - self.status = 'Draft' - return - - if not status: - precision = self.precision("outstanding_amount") - args = [ - self.name, - self.outstanding_amount, - self.is_return, - self.due_date, - self.docstatus, - precision - ] - self.status = get_status(args) - - if update: - self.db_set('status', self.status, update_modified = update_modified) - def set_missing_values(self, for_validate=False): if not self.credit_to: self.credit_to = get_party_account("Supplier", self.supplier, self.company) @@ -985,34 +964,6 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() -def get_status(*args): - purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0] - - outstanding_amount = flt(outstanding_amount, precision) - due_date = getdate(due_date) - now_date = getdate() - - if docstatus == 2: - status = "Cancelled" - elif docstatus == 1: - if outstanding_amount > 0 and due_date < now_date: - status = "Overdue" - elif outstanding_amount > 0 and due_date >= now_date: - status = "Unpaid" - #Check if outstanding amount is 0 due to debit note issued against invoice - elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}): - status = "Debit Note Issued" - elif is_return == 1: - status = "Return" - elif outstanding_amount <=0: - status = "Paid" - else: - status = "Submitted" - else: - status = "Draft" - - return status - def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) @@ -1073,3 +1024,6 @@ def block_invoice(name, release_date, hold_comment=None): def make_inter_company_sales_invoice(source_name, target_doc=None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) + +def on_doctype_update(): + frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"]) \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index c0e128567f9..16e918cc728 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -423,7 +423,10 @@ def make_invoice(pos_profile, doc_list={}, email_queue_list={}, customers_list={ name_list.append(name) email_queue = make_email_queue(email_queue_list) - pos_profile = json.loads(pos_profile) + + if isinstance(pos_profile, string_types): + pos_profile = json.loads(pos_profile) + customers = get_customers_list(pos_profile) return { 'invoice': name_list, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ed34127fbb3..fcddab40b8f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1217,18 +1217,38 @@ class SalesInvoice(SellingController): self.status = 'Draft' return + precision = self.precision("outstanding_amount") + outstanding_amount = flt(self.outstanding_amount, precision) + due_date = getdate(self.due_date) + nowdate = getdate() + + discounting_status = None + if self.is_discounted: + discountng_status = get_discounting_status(self.name) + if not status: - precision = self.precision("outstanding_amount") - args = [ - self.name, - self.outstanding_amount, - self.is_discounted, - self.is_return, - self.due_date, - self.docstatus, - precision, - ] - self.status = get_status(args) + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': + self.status = "Overdue and Discounted" + elif outstanding_amount > 0 and due_date < nowdate: + self.status = "Overdue" + elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': + self.status = "Unpaid and Discounted" + elif outstanding_amount > 0 and due_date >= nowdate: + self.status = "Unpaid" + #Check if outstanding amount is 0 due to credit note issued against invoice + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + self.status = "Credit Note Issued" + elif self.is_return == 1: + self.status = "Return" + elif outstanding_amount<=0: + self.status = "Paid" + else: + self.status = "Submitted" + else: + self.status = "Draft" if update: self.db_set('status', self.status, update_modified = update_modified) @@ -1253,42 +1273,6 @@ def get_discounting_status(sales_invoice): return status -def get_status(*args): - sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0] - - discounting_status = None - if is_discounted: - discounting_status = get_discounting_status(sales_invoice) - - outstanding_amount = flt(outstanding_amount, precision) - due_date = getdate(due_date) - now_date = getdate() - - if docstatus == 2: - status = "Cancelled" - elif docstatus == 1: - if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed': - status = "Overdue and Discounted" - elif outstanding_amount > 0 and due_date < now_date: - status = "Overdue" - elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed': - status = "Unpaid and Discounted" - elif outstanding_amount > 0 and due_date >= now_date: - status = "Unpaid" - #Check if outstanding amount is 0 due to credit note issued against invoice - elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}): - status = "Credit Note Issued" - elif is_return == 1: - status = "Return" - elif outstanding_amount <=0: - status = "Paid" - else: - status = "Submitted" - else: - status = "Draft" - - return status - def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a2a47b3a19c..267e52a477b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -728,7 +728,7 @@ class TestSalesInvoice(unittest.TestCase): def test_make_pos_invoice(self): from erpnext.accounts.doctype.sales_invoice.pos import make_invoice - make_pos_profile() + pos_profile = make_pos_profile() pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1") pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True) @@ -744,7 +744,7 @@ class TestSalesInvoice(unittest.TestCase): pos.append("taxes", tax) invoice_data = [{'09052016142': pos}] - si = make_invoice(invoice_data).get('invoice') + si = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si[0], '09052016142') sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1}) @@ -762,7 +762,7 @@ class TestSalesInvoice(unittest.TestCase): if allow_negative_stock: frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0) - make_pos_profile() + pos_profile = make_pos_profile() timestamp = cint(time.time()) item = make_item("_Test POS Item") @@ -776,7 +776,7 @@ class TestSalesInvoice(unittest.TestCase): {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}] invoice_data = [{timestamp: pos}] - si = make_invoice(invoice_data).get('invoice') + si = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si[0], timestamp) sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) @@ -785,7 +785,7 @@ class TestSalesInvoice(unittest.TestCase): timestamp = cint(time.time()) pos["offline_pos_name"] = timestamp invoice_data = [{timestamp: pos}] - si1 = make_invoice(invoice_data).get('invoice') + si1 = make_invoice(pos_profile, invoice_data).get('invoice') self.assertEqual(si1[0], timestamp) sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp}) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 6d53530321f..5ba455c1315 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -136,12 +136,11 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def make_entry(args, adv_adj, update_outstanding, from_repost=False): - args.update({"doctype": "GL Entry"}) - gle = frappe.get_doc(args) + gle = frappe.new_doc("GL Entry") + gle.update(args) gle.flags.ignore_permissions = 1 gle.flags.from_repost = from_repost gle.validate() - gle.flags.ignore_permissions = True gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.flags.ignore_validate = True diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index b465a106f0e..4d0520abc81 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -69,6 +69,17 @@ status_map = { ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], ], + "Purchase Invoice": [ + ["Draft", None], + ["Submitted", "eval:self.docstatus==1"], + ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], + ["Return", "eval:self.is_return==1 and self.docstatus==1"], + ["Debit Note Issued", + "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], + ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], + ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], + ["Cancelled", "eval:self.docstatus==2"], + ], "Material Request": [ ["Draft", None], ["Stopped", "eval:self.status == 'Stopped'"], diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index 524f3eb5b17..fe13d63955f 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.utils import random_string from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_charts_for_country -test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component"] +test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"] test_dependencies = ["Fiscal Year"] test_records = frappe.get_test_records('Company') diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d2e04914017..cb0eb406df6 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -183,12 +183,17 @@ class Item(WebsiteGenerator): # default warehouse, or Stores for default in self.item_defaults or [frappe._dict({'company': frappe.defaults.get_defaults().company})]: default_warehouse = (default.default_warehouse - or frappe.db.get_single_value('Stock Settings', 'default_warehouse') - or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})) + or frappe.db.get_single_value('Stock Settings', 'default_warehouse')) + if default_warehouse: + warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company") + + if not default_warehouse or warehouse_company != default.company: + default_warehouse = frappe.db.get_value('Warehouse', + {'warehouse_name': _('Stores'), 'company': default.company}) if default_warehouse: stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse, qty=self.opening_stock, - rate=self.valuation_rate, company=default.company) + rate=self.valuation_rate, company=default.company) stock_entry.add_comment("Comment", _("Opening Stock")) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 7ea84e99131..6e19b71cac8 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -53,7 +53,7 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) def test_batched_serial_no_purchase(self): - item = frappe.get_doc("Item", { 'item_name': 'Batched Serialized Item' }) + item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'}) if not item: item = create_item("Batched Serialized Item") item.has_batch_no = 1 @@ -62,6 +62,8 @@ class TestPurchaseReceipt(unittest.TestCase): item.batch_number_series = "BS-BATCH-.##" item.serial_no_series = "BS-.####" item.save() + else: + item = frappe.get_doc("Item", {'item_name': 'Batched Serialized Item'}) pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500) @@ -302,6 +304,8 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(serial_no, frappe.db.get_value("Serial No", {"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name}, "name")) + pr_doc.cancel() + item_code = "Test Auto Created Serial No" if not frappe.db.exists("Item", item_code): item = make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###")) @@ -316,9 +320,9 @@ class TestPurchaseReceipt(unittest.TestCase): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0}) + item_code = frappe.db.get_value('Item', {'has_serial_no': 1, 'is_fixed_asset': 0, "has_batch_no": 0}) if not item_code: - item = make_item("Test Serial Item 1", dict(has_serial_no=1)) + item = make_item("Test Serial Item 1", dict(has_serial_no=1, has_batch_no=0)) item_code = item.name serial_no = random_string(5) From cd71114c1575756eccee1ddae9d4c160d06b9396 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 16 Mar 2020 11:14:32 +0100 Subject: [PATCH 52/74] fix(accounts): add account_type and tax_rate to some VAT accounts (#20941) * Add account_type and tax rate to some VAT accounts * fix indentation --- ..._kontenplan_SKR04_with_account_number.json | 142 ++++++++++-------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index adfa9f89e23..275374a4b70 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -406,11 +406,11 @@ "is_group": 1, "Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": { "account_number": "9960" - }, - "Debitoren": { - "is_group": 1, - "account_number": "10000" - }, + }, + "Debitoren": { + "is_group": 1, + "account_number": "10000" + }, "Forderungen aus Lieferungen und Leistungen": { "account_number": "1200", "account_type": "Receivable" @@ -663,16 +663,22 @@ "account_number": "1400" }, "Abziehbare Vorsteuer 7 %": { - "account_number": "1401" + "account_number": "1401", + "account_type": "Tax", + "tax_rate": 7.0 }, "Abziehbare Vorsteuer aus innergem. Erwerb": { "account_number": "1402" }, "Abziehbare Vorsteuer aus innergem. Erwerb 19%": { - "account_number": "1404" + "account_number": "1404", + "account_type": "Tax", + "tax_rate": 19.0 }, "Abziehbare Vorsteuer 19 %": { - "account_number": "1406" + "account_number": "1406", + "account_type": "Tax", + "tax_rate": 19.0 }, "Abziehbare Vorsteuer nach \u00a7 13b UStG 19 %": { "account_number": "1407" @@ -1197,15 +1203,15 @@ "is_group": 1, "Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": { "account_number": "9964" - }, - "Kreditoren": { - "account_number": "70000", - "is_group": 1, - "Wareneingangs-­Verrechnungskonto" : { - "account_number": "70001", - "account_type": "Stock Received But Not Billed" - } - }, + }, + "Kreditoren": { + "account_number": "70000", + "is_group": 1, + "Wareneingangs-­Verrechnungskonto" : { + "account_number": "70001", + "account_type": "Stock Received But Not Billed" + } + }, "Verb. aus Lieferungen und Leistungen": { "account_number": "3300", "account_type": "Payable" @@ -1488,17 +1494,21 @@ }, "Umsatzsteuer 7 %": { "account_number": "3801", - "account_type": "Tax" + "account_type": "Tax", + "tax_rate": 7.0 }, "Umsatzsteuer aus innergem. Erwerb": { "account_number": "3802" }, "Umsatzsteuer aus innergem. Erwerb 19 %": { - "account_number": "3804" + "account_number": "3804", + "account_type": "Tax", + "tax_rate": 19.0 }, "Umsatzsteuer 19 %": { "account_number": "3806", - "account_type": "Tax" + "account_type": "Tax", + "tax_rate": 19.0 }, "Umsatzsteuer aus im Inland steuerpfl. EU-Lieferungen": { "account_number": "3807" @@ -2295,49 +2305,49 @@ }, "6 - sonstige betriebliche Ertr\u00e4ge": { "root_type": "Income", - "is_group": 1, - "Erhaltene Boni (Gruppe)": { - "is_group": 1, - "Erhaltene Boni 7 % Vorsteuer": { - "account_number": "5750" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5753" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5754" - }, - "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5755" - }, - "Erhaltene Boni 19 % Vorsteuer": { - "account_number": "5760" - }, - "Erhaltene Boni": { - "account_number": "5769" - } - }, - "Erhaltene Rabatte (Gruppe)": { - "is_group": 1, - "Erhaltene Rabatte": { - "account_number": "5770" - }, - "Erhaltene Rabatte 7 % Vorsteuer": { - "account_number": "5780" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { - "account_number": "5783" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { - "account_number": "5784" - }, - "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { - "account_number": "5785" - }, - "Erhaltene Rabatte 19 % Vorsteuer": { - "account_number": "5790" - } - }, + "is_group": 1, + "Erhaltene Boni (Gruppe)": { + "is_group": 1, + "Erhaltene Boni 7 % Vorsteuer": { + "account_number": "5750" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5753" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5754" + }, + "Erhaltene Boni aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5755" + }, + "Erhaltene Boni 19 % Vorsteuer": { + "account_number": "5760" + }, + "Erhaltene Boni": { + "account_number": "5769" + } + }, + "Erhaltene Rabatte (Gruppe)": { + "is_group": 1, + "Erhaltene Rabatte": { + "account_number": "5770" + }, + "Erhaltene Rabatte 7 % Vorsteuer": { + "account_number": "5780" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe": { + "account_number": "5783" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 7% Vorsteuer": { + "account_number": "5784" + }, + "Erhaltene Rabatte aus Einkauf Roh-, Hilfs- und Betriebsstoffe 19% Vorsteuer": { + "account_number": "5785" + }, + "Erhaltene Rabatte 19 % Vorsteuer": { + "account_number": "5790" + } + }, "Andere aktivierte Eigenleistungen": { "account_number": "4820" }, @@ -2660,8 +2670,8 @@ }, "7 - sonstige betriebliche Aufwendungen": { "root_type": "Expense", - "is_group": 1, - "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { + "is_group": 1, + "Erl\u00f6sschm\u00e4lerungen (Gruppe)": { "is_group": 1, "Erl\u00f6sschm\u00e4lerungen": { "account_number": "4700" @@ -2744,7 +2754,7 @@ "Gew\u00e4hrte Rabatte 19 % USt": { "account_number": "4790" } - }, + }, "Sonstige betriebliche Aufwendungen": { "account_number": "6300" }, From 6f3941c9cc67e7d445aa6a82a52d383d56745fe3 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Mon, 16 Mar 2020 15:49:50 +0530 Subject: [PATCH 53/74] fix: remove make_purchase_invoice from demo script (#20906) currently demo fails with the following error: frappe@bf54a9834cfe:/workspace/development/frappe-bench$ bench --site test1.localhost execute erpnext.demo.demo.make Traceback (most recent call last): File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "/usr/lib/python3.7/runpy.py", line 85, in _run_code exec(code, run_globals) File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 97, in main() File "/workspace/development/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", line 18, in main click.Group(commands=commands)(prog_name='bench') File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 764, in __call__ return self.main(*args, **kwargs) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 717, in main rv = self.invoke(ctx) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 1137, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 956, in invoke return ctx.invoke(self.callback, **ctx.params) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/core.py", line 555, in invoke return callback(*args, **kwargs) File "/workspace/development/frappe-bench/env/lib/python3.7/site-packages/click/decorators.py", line 17, in new_func return f(get_current_context(), *args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/__init__.py", line 25, in _func ret = f(frappe._dict(ctx.obj), *args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/commands/utils.py", line 145, in execute ret = frappe.get_attr(method)(*args, **kwargs) File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 1042, in get_attr return getattr(get_module(modulename), methodname) File "/workspace/development/frappe-bench/apps/frappe/frappe/__init__.py", line 823, in get_module return importlib.import_module(modulename) File "/workspace/development/frappe-bench/env/lib/python3.7/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "", line 1006, in _gcd_import File "", line 983, in _find_and_load File "", line 967, in _find_and_load_unlocked File "", line 677, in _load_unlocked File "", line 728, in exec_module File "", line 219, in _call_with_frames_removed File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/demo.py", line 6, in from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset File "/workspace/development/frappe-bench/apps/erpnext/erpnext/demo/user/fixed_asset.py", line 9, in from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice ImportError: cannot import name 'make_purchase_invoice' from 'erpnext.assets.doctype.asset.asset' (/workspace/development/frappe-bench/apps/erpnext/erpnext/assets/doctype/asset/asset.py) Signed-off-by: Chinmay D. Pai --- erpnext/demo/user/fixed_asset.py | 39 +++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/erpnext/demo/user/fixed_asset.py b/erpnext/demo/user/fixed_asset.py index e6d16872024..dc094e1c918 100644 --- a/erpnext/demo/user/fixed_asset.py +++ b/erpnext/demo/user/fixed_asset.py @@ -6,46 +6,28 @@ from __future__ import unicode_literals import frappe from frappe.utils.make_random import get_random -from erpnext.assets.doctype.asset.asset import make_purchase_invoice, make_sales_invoice +from erpnext.assets.doctype.asset.asset import make_sales_invoice from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset + def work(): frappe.set_user(frappe.db.get_global('demo_accounts_user')) - asset_list = make_asset_purchase_entry() - - if not asset_list: - # fixed_asset.work() already run - return - # Enable booking asset depreciation entry automatically frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) - + # post depreciation entries as on today post_depreciation_entries() - + # scrap a random asset frappe.db.set_value("Company", "Wind Power LLC", "disposal_account", "Gain/Loss on Asset Disposal - WPL") - + asset = get_random_asset() scrap_asset(asset.name) - - # Sell a random asset - sell_an_asset() -def make_asset_purchase_entry(): - asset_list = frappe.get_all("Asset", filters={"purchase_invoice": ["in", ("", None)]}, - fields=["name", "item_code", "gross_purchase_amount", "company", "purchase_date"]) - - # make purchase invoice - for asset in asset_list: - pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount, - asset.company, asset.purchase_date) - pi.supplier = get_random("Supplier") - pi.save() - pi.submit() - - return asset_list + # Sell a random asset + sell_an_asset() + def sell_an_asset(): asset = get_random_asset() @@ -55,8 +37,9 @@ def sell_an_asset(): if asset.value_after_depreciation else asset.gross_purchase_amount * 0.9 si.save() si.submit() - + + def get_random_asset(): return frappe.db.sql(""" select name, item_code, value_after_depreciation, gross_purchase_amount - from `tabAsset` + from `tabAsset` where docstatus=1 and status not in ("Scrapped", "Sold") order by rand() limit 1""", as_dict=1)[0] From 46053f6c8f572195bf665adf346d10e5c9139d69 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 16 Mar 2020 15:52:00 +0530 Subject: [PATCH 54/74] fix: fix options of currency fieldtype in Expense Taxes and Charges (#20910) --- .../expense_taxes_and_charges/expense_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 9bf69daf65a..1b1889b205f 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -70,7 +70,7 @@ "label": "Amount", "oldfieldname": "tax_amount", "oldfieldtype": "Currency", - "options": "currency" + "options": "Company:company:default_currency" }, { "columns": 2, @@ -80,7 +80,7 @@ "label": "Total", "oldfieldname": "total", "oldfieldtype": "Currency", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { From 37886a72efc4f9f9a2ffccd9fa1e396508230abd Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 16 Mar 2020 15:57:42 +0530 Subject: [PATCH 55/74] fix(HR): Leave application company field made read-only and filter in Monthly attendance sheet (#20911) * fix(HR): Leave application company field made Readonly * fix: in monthly report attandance employee filter based on company * fix: minor typo --- .../leave_application/leave_application.json | 666 +++++++++--------- .../monthly_attendance_sheet.py | 6 +- 2 files changed, 338 insertions(+), 334 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index cdb1add3918..460be514b59 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -1,332 +1,336 @@ { - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-02-20 11:18:11", - "description": "Apply / Approve Leaves", - "doctype": "DocType", - "document_type": "Document", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "column_break_4", - "leave_type", - "department", - "leave_balance", - "section_break_5", - "from_date", - "to_date", - "half_day", - "half_day_date", - "total_leave_days", - "column_break1", - "description", - "section_break_7", - "leave_approver", - "leave_approver_name", - "column_break_18", - "status", - "salary_slip", - "sb10", - "posting_date", - "follow_via_email", - "color", - "column_break_17", - "company", - "letter_head", - "amended_from" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-LAP-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "in_standard_filter": 1, - "label": "Employee", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "leave_type", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "options": "Leave Type", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "leave_balance", - "fieldtype": "Float", - "label": "Leave Balance Before Application", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "from_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "From Date", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "reqd": 1, - "search_index": 1 - }, - { - "default": "0", - "fieldname": "half_day", - "fieldtype": "Check", - "label": "Half Day" - }, - { - "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", - "fieldname": "half_day_date", - "fieldtype": "Date", - "label": "Half Day Date" - }, - { - "fieldname": "total_leave_days", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Total Leave Days", - "no_copy": 1, - "precision": "1", - "read_only": 1 - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Reason" - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "fieldname": "leave_approver", - "fieldtype": "Link", - "label": "Leave Approver", - "options": "User" - }, - { - "fieldname": "leave_approver_name", - "fieldtype": "Data", - "label": "Leave Approver Name", - "read_only": 1 - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Open\nApproved\nRejected\nCancelled" - }, - { - "fieldname": "sb10", - "fieldtype": "Section Break" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "no_copy": 1, - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "allow_on_submit": 1, - "default": "1", - "fieldname": "follow_via_email", - "fieldtype": "Check", - "label": "Follow via Email", - "print_hide": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "salary_slip", - "fieldtype": "Link", - "label": "Salary Slip", - "options": "Salary Slip", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "letter_head", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Letter Head", - "options": "Letter Head", - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "fieldname": "color", - "fieldtype": "Color", - "label": "Color", - "print_hide": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "options": "Leave Application", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-calendar", - "idx": 1, - "is_submittable": 1, - "max_attachments": 3, - "modified": "2019-08-13 13:32:04.860848", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Application", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "role": "All" - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 1, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "HR User", - "write": 1 - }, - { - "permlevel": 1, - "read": 1, - "report": 1, - "role": "Leave Approver", - "write": 1 - } - ], - "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "employee_name" - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-02-20 11:18:11", + "description": "Apply / Approve Leaves", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "column_break_4", + "leave_type", + "department", + "leave_balance", + "section_break_5", + "from_date", + "to_date", + "half_day", + "half_day_date", + "total_leave_days", + "column_break1", + "description", + "section_break_7", + "leave_approver", + "leave_approver_name", + "column_break_18", + "status", + "salary_slip", + "sb10", + "posting_date", + "follow_via_email", + "color", + "column_break_17", + "company", + "letter_head", + "amended_from" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-LAP-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "options": "Leave Type", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "leave_balance", + "fieldtype": "Float", + "label": "Leave Balance Before Application", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "half_day", + "fieldtype": "Check", + "label": "Half Day" + }, + { + "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", + "fieldname": "half_day_date", + "fieldtype": "Date", + "label": "Half Day Date" + }, + { + "fieldname": "total_leave_days", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Leave Days", + "no_copy": 1, + "precision": "1", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Reason" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User" + }, + { + "fieldname": "leave_approver_name", + "fieldtype": "Data", + "label": "Leave Approver Name", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Open\nApproved\nRejected\nCancelled", + "permlevel": 1 + }, + { + "fieldname": "sb10", + "fieldtype": "Section Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "allow_on_submit": 1, + "default": "1", + "fieldname": "follow_via_email", + "fieldtype": "Check", + "label": "Follow via Email", + "print_hide": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "color", + "fieldtype": "Color", + "label": "Color", + "print_hide": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Leave Application", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-calendar", + "idx": 1, + "is_submittable": 1, + "links": [], + "max_attachments": 3, + "modified": "2020-03-10 22:40:43.487721", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Application", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "All" + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 1, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "HR User", + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "write": 1 + } + ], + "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "employee_name" +} diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 1e9c83bf3e6..aca3a175057 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -13,7 +13,7 @@ def execute(filters=None): conditions, filters = get_conditions(filters) columns = get_columns(filters) att_map = get_attendance_list(conditions, filters) - emp_map = get_employee_details() + emp_map = get_employee_details(filters) holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]] default_holiday_list = frappe.get_cached_value('Company', filters.get("company"), "default_holiday_list") @@ -131,10 +131,10 @@ def get_conditions(filters): return conditions, filters -def get_employee_details(): +def get_employee_details(filters): emp_map = frappe._dict() for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company, - holiday_list from tabEmployee""", as_dict=1): + holiday_list from tabEmployee where company = "%s" """ % (filters.get("company")), as_dict=1): emp_map.setdefault(d.name, d) return emp_map From 43a0011987a0badcbc39c1c26f80192e7d200199 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 16 Mar 2020 16:28:54 +0530 Subject: [PATCH 56/74] fix: serial_no_selector showing for non serialized batched item (#20852) --- erpnext/stock/doctype/stock_entry/stock_entry.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ea27c6f5d5b..92cc8f65047 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -886,10 +886,8 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { } } - if(item && item.has_serial_no - && frm.doc.purpose === 'Material Receipt') { - return; - } + if(item && !item.has_serial_no && item.has_batch_no) return; + if (frm.doc.purpose === 'Material Receipt') return; frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { new erpnext.SerialNoBatchSelector({ From 11fb93d022db092ea2e9ab4f6cb3f0d82f655d23 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 16 Mar 2020 16:54:34 +0530 Subject: [PATCH 57/74] fix: Add bank transaction in module view (#20823) --- erpnext/config/accounts.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index ab75f211c04..3df5fcc3ebd 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -181,6 +181,11 @@ def get_data(): "name": "Payment Reconciliation", "description": _("Match non-linked Invoices and Payments.") }, + { + "type": "doctype", + "label": _("Invoice Discounting"), + "name": "Invoice Discounting", + }, { "type": "doctype", "label": _("Update Bank Transaction Dates"), @@ -189,8 +194,9 @@ def get_data(): }, { "type": "doctype", - "label": _("Invoice Discounting"), - "name": "Invoice Discounting", + "label": _("Bank Transaction"), + "name": "Bank Transaction", + "doctype": "Bank Transaction" }, { "type": "report", From 5c760175d199a0e579073440d316773c4f0f4377 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 16 Mar 2020 17:26:02 +0530 Subject: [PATCH 58/74] fix: deletion of auto-created batch (#20953) --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d13edc34573..c6d0c7c0a6e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -239,7 +239,7 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue - serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})] if serial_nos: frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) From e7a6a4b009508216f9976655977abf9de1ef2ca4 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 16 Mar 2020 17:48:21 +0530 Subject: [PATCH 59/74] fix: is_pos gets reset on making s_inv from s_ord (#20887) * s_inv has is_pos default set as 1 --- erpnext/selling/doctype/sales_order/sales_order.py | 1 - erpnext/stock/doctype/delivery_note/delivery_note.py | 1 - 2 files changed, 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4a365004da8..9e4a71d60e5 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -645,7 +645,6 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): target.set_advances() def set_missing_values(source, target): - target.is_pos = 0 target.ignore_pricing_rule = 1 target.flags.ignore_permissions = True target.run_method("set_missing_values") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 39aad2e0071..3f74b54ce80 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -413,7 +413,6 @@ def make_sales_invoice(source_name, target_doc=None): invoiced_qty_map = get_invoiced_qty_map(source_name) def set_missing_values(source, target): - target.is_pos = 0 target.ignore_pricing_rule = 1 target.run_method("set_missing_values") target.run_method("set_po_nos") From 281a9fdf0735e9797014cfc18424d9044bc7d83e Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 16 Mar 2020 17:57:15 +0530 Subject: [PATCH 60/74] fix: handle error handling (#20814) --- erpnext/stock/doctype/delivery_trip/delivery_trip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 77d322ed28d..209b1ef1468 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -238,7 +238,7 @@ class DeliveryTrip(Document): try: directions = maps_client.directions(**directions_data) except Exception as e: - frappe.throw(_(e)) + frappe.throw(_(str(e))) return directions[0] if directions else False From d2a28370347a9473628ab236ce14e8e834ae9a22 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 16 Mar 2020 21:42:51 +0530 Subject: [PATCH 61/74] fix: bom replace tool issue (#20841) Co-authored-by: Nabin Hait --- erpnext/manufacturing/doctype/bom/bom.py | 8 ++++++ .../bom_update_tool/bom_update_tool.py | 27 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 7f7ffa14bfa..b2a0b18db5e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -487,6 +487,14 @@ class BOM(WebsiteGenerator): self.scrap_material_cost = total_sm_cost self.base_scrap_material_cost = base_total_sm_cost + def update_new_bom(self, old_bom, new_bom, rate): + for d in self.get("items"): + if d.bom_no != old_bom: continue + + d.bom_no = new_bom + d.rate = rate + d.amount = (d.stock_qty or d.qty) * rate + def update_exploded_items(self): """ Update Flat BOM, following will be correct data""" self.get_exploded_items() diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 31a9fdb28ab..2758a423716 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -14,10 +14,13 @@ import click class BOMUpdateTool(Document): def replace_bom(self): self.validate_bom() - self.update_new_bom() + + unit_cost = get_new_bom_unit_cost(self.new_bom) + self.update_new_bom(unit_cost) + frappe.cache().delete_key('bom_children') bom_list = self.get_parent_boms(self.new_bom) - updated_bom = [] + with click.progressbar(bom_list) as bom_list: pass for bom in bom_list: @@ -26,7 +29,9 @@ class BOMUpdateTool(Document): # this is only used for versioning and we do not want # to make separate db calls by using load_doc_before_save # which proves to be expensive while doing bulk replace - bom_obj._doc_before_save = bom_obj.as_dict() + bom_obj._doc_before_save = bom_obj + bom_obj.update_new_bom(self.current_bom, self.new_bom, unit_cost) + bom_obj.update_exploded_items() bom_obj.calculate_cost() bom_obj.update_parent_cost() bom_obj.db_update() @@ -43,14 +48,10 @@ class BOMUpdateTool(Document): != frappe.db.get_value("BOM", self.new_bom, "item"): frappe.throw(_("The selected BOMs are not for the same item")) - def update_new_bom(self): - new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity` - FROM `tabBOM` WHERE name = %s""", self.new_bom) - new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0 - + def update_new_bom(self, unit_cost): frappe.db.sql("""update `tabBOM Item` set bom_no=%s, rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""", - (self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom)) + (self.new_bom, unit_cost, unit_cost, self.current_bom)) def get_parent_boms(self, bom, bom_list=[]): data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item` @@ -65,12 +66,18 @@ class BOMUpdateTool(Document): return list(set(bom_list)) +def get_new_bom_unit_cost(bom): + new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity` + FROM `tabBOM` WHERE name = %s""", bom) + + return flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0 + @frappe.whitelist() def enqueue_replace_bom(args): if isinstance(args, string_types): args = json.loads(args) - frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=4000) + frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args, timeout=40000) frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes.")) @frappe.whitelist() From 0dcd5a0f341734b7a8975c5f63bbf5144396d3a5 Mon Sep 17 00:00:00 2001 From: Don-Leopardo <46027152+Don-Leopardo@users.noreply.github.com> Date: Mon, 16 Mar 2020 14:05:01 -0300 Subject: [PATCH 62/74] fix: sql injection (#20817) --- .../doctype/bank_guarantee/bank_guarantee.py | 4 ++++ .../patient_appointment/patient_appointment.py | 4 ++-- erpnext/hr/doctype/department/department.py | 13 +++++++++---- erpnext/regional/india/utils.py | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index 9aa2ee271a4..f28a07431fe 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe import _ +from frappe.desk.search import sanitize_searchfield class BankGuarantee(Document): def validate(self): @@ -22,5 +23,8 @@ class BankGuarantee(Document): @frappe.whitelist() def get_vouchar_detials(column_list, doctype, docname): + column_list = json.loads(column_list) + for col in column_list: + sanitize_searchfield(col) return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s''' .format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0] diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 7aa41c546c0..12248fcdce3 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -386,5 +386,5 @@ def get_procedure_prescribed(patient): return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner, ct.encounter_date, pp.practitioner, pp.date, pp.department from `tabPatient Encounter` ct, `tabProcedure Prescription` pp - where ct.patient='{0}' and pp.parent=ct.name and pp.appointment_booked=0 - order by ct.creation desc""".format(patient)) + where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0 + order by ct.creation desc""", {"patient": patient}) diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py index 9b2b5817660..2cef5092767 100644 --- a/erpnext/hr/doctype/department/department.py +++ b/erpnext/hr/doctype/department/department.py @@ -48,12 +48,17 @@ def get_abbreviated_name(name, company): @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False): condition = '' + var_dict = { + "name": get_root_of("Department"), + "parent": parent, + "company": company, + } if company == parent: - condition = "name='{0}'".format(get_root_of("Department")) + condition = "name=%(name)s" elif company: - condition = "parent_department='{0}' and company='{1}'".format(parent, company) + condition = "parent_department=%(parent)s and company=%(company)s" else: - condition = "parent_department = '{0}'".format(parent) + condition = "parent_department = %(parent)s" return frappe.db.sql(""" select @@ -62,7 +67,7 @@ def get_children(doctype, parent=None, company=None, is_root=False): from `tab{doctype}` where {condition} - order by name""".format(doctype=doctype, condition=condition), as_dict=1) + order by name""".format(doctype=doctype, condition=condition), var_dict, as_dict=1) @frappe.whitelist() def add_node(): diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 0f9156a6b4c..831985fc7cc 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -464,7 +464,7 @@ def get_gstins_for_company(company): `tabDynamic Link`.parent = `tabAddress`.name and `tabDynamic Link`.parenttype = 'Address' and `tabDynamic Link`.link_doctype = 'Company' and - `tabDynamic Link`.link_name = '{0}'""".format(company)) + `tabDynamic Link`.link_name = %(company)s""", {"company": company}) return company_gstins def get_address_details(data, doc, company_address, billing_address): From dd8059a3aa5ff1c6f5d7daebf5826061848200b8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Mar 2020 22:36:32 +0530 Subject: [PATCH 63/74] fix: Mode of payment not fetcched in Item wise purchase register --- .../item_wise_purchase_register.py | 7 +++++-- .../item_wise_sales_register/item_wise_sales_register.py | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 4523f66deb4..d1547f8d2cb 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -201,7 +201,8 @@ def get_columns(additional_table_columns, filters): { 'label': _('Mode Of Payment'), 'fieldname': 'mode_of_payment', - 'fieldtype': 'Data', + 'fieldtype': 'Link', + 'options': 'Mode of Payment', 'width': 120 }, { @@ -309,6 +310,8 @@ def get_items(filters, additional_query_columns): if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) + else: + additional_query_columns = '' return frappe.db.sql(""" select @@ -320,7 +323,7 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, `tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`, - `tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0} + `tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0} from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and `tabPurchase Invoice`.docstatus = 1 %s diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 786e04dd5aa..0c8957ae441 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -373,6 +373,8 @@ def get_items(filters, additional_query_columns): if additional_query_columns: additional_query_columns = ', ' + ', '.join(additional_query_columns) + else: + additional_query_columns = '' return frappe.db.sql(""" select From 7c0e89d7857baeb32ab331972101e7a5c9070bed Mon Sep 17 00:00:00 2001 From: nmimsnikhil <56826544+nmimsnikhil@users.noreply.github.com> Date: Mon, 16 Mar 2020 22:41:23 +0530 Subject: [PATCH 64/74] fix: added a Reason for Rejection field for each items (#20828) * fix: added a Reason for Rejection field for each items * Update delivery_note_item.json Co-authored-by: Nabin Hait --- .../delivery_note_item.json | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 782ac84e57d..a3386fce193 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -81,6 +81,8 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "reason_for_return_section_break", + "reason_for_return", "section_break_72", "page_break" ], @@ -699,12 +701,26 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "depends_on": "eval:parent.is_return==1", + "fieldname": "reason_for_return", + "fieldtype": "Small Text", + "label": "Reason for Return", + "no_copy": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.is_return==1", + "fieldname": "reason_for_return_section_break", + "fieldtype": "Section Break", + "label": "Reason For Return" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:18:33.131672", + "modified": "2020-03-06 14:18:33.131672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", @@ -712,4 +728,4 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From b9e1d9ad7d1fec9a29f083cbc581727b44129301 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 16 Mar 2020 22:44:17 +0530 Subject: [PATCH 65/74] Deduction based on earnins/gross pay (#20935) * fix: deductions calculation based on gross pay * test: salary structure deduction based on gross pay Co-authored-by: Nabin Hait --- .../additional_salary/additional_salary.py | 6 ++- erpnext/hr/doctype/salary_slip/salary_slip.py | 48 ++++++++++--------- .../salary_structure/test_salary_structure.py | 24 +++++++++- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index 8498b3d277d..bc7dcee55e4 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -39,19 +39,21 @@ class AdditionalSalary(Document): return amount_per_day * no_of_days @frappe.whitelist() -def get_additional_salary_component(employee, start_date, end_date): +def get_additional_salary_component(employee, start_date, end_date, component_type): additional_components = frappe.db.sql(""" select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date from `tabAdditional Salary` where employee=%(employee)s and docstatus = 1 and payroll_date between %(from_date)s and %(to_date)s + and type = %(component_type)s group by salary_component, overwrite_salary_structure_amount order by salary_component, overwrite_salary_structure_amount """, { 'employee': employee, 'from_date': start_date, - 'to_date': end_date + 'to_date': end_date, + 'component_type': "Earning" if component_type == "earnings" else "Deduction" }, as_dict=1) additional_components_list = [] diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 0171643b13c..e126db5f5b1 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -298,9 +298,11 @@ class SalarySlip(TransactionBase): def calculate_net_pay(self): if self.salary_structure: - self.calculate_component_amounts() - + self.calculate_component_amounts("earnings") self.gross_pay = self.get_component_totals("earnings") + + if self.salary_structure: + self.calculate_component_amounts("deductions") self.total_deduction = self.get_component_totals("deductions") self.set_loan_repayment() @@ -308,25 +310,27 @@ class SalarySlip(TransactionBase): self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.rounded_total = rounded(self.net_pay) - def calculate_component_amounts(self): + def calculate_component_amounts(self, component_type): if not getattr(self, '_salary_structure_doc', None): self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure) payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) - self.add_structure_components() - self.add_employee_benefits(payroll_period) - self.add_additional_salary_components() - self.add_tax_components(payroll_period) - self.set_component_amounts_based_on_payment_days() + self.add_structure_components(component_type) + self.add_additional_salary_components(component_type) + if component_type == "earnings": + self.add_employee_benefits(payroll_period) + else: + self.add_tax_components(payroll_period) - def add_structure_components(self): + self.set_component_amounts_based_on_payment_days(component_type) + + def add_structure_components(self, component_type): data = self.get_data_for_eval() - for key in ('earnings', 'deductions'): - for struct_row in self._salary_structure_doc.get(key): - amount = self.eval_condition_and_formula(struct_row, data) - if amount and struct_row.statistical_component == 0: - self.update_component_row(struct_row, amount, key) + for struct_row in self._salary_structure_doc.get(component_type): + amount = self.eval_condition_and_formula(struct_row, data) + if amount and struct_row.statistical_component == 0: + self.update_component_row(struct_row, amount, component_type) def get_data_for_eval(self): '''Returns data for evaluating formula''' @@ -399,14 +403,15 @@ class SalarySlip(TransactionBase): amount = last_benefit.amount self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") - def add_additional_salary_components(self): - additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date) + def add_additional_salary_components(self, component_type): + additional_components = get_additional_salary_component(self.employee, + self.start_date, self.end_date, component_type) if additional_components: for additional_component in additional_components: amount = additional_component.amount overwrite = additional_component.overwrite - key = "earnings" if additional_component.type == "Earning" else "deductions" - self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite) + self.update_component_row(frappe._dict(additional_component.struct_row), amount, + component_type, overwrite=overwrite) def add_tax_components(self, payroll_period): # Calculate variable_based_on_taxable_salary after all components updated in salary slip @@ -735,7 +740,7 @@ class SalarySlip(TransactionBase): total += d.amount return total - def set_component_amounts_based_on_payment_days(self): + def set_component_amounts_based_on_payment_days(self, component_type): joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) @@ -745,9 +750,8 @@ class SalarySlip(TransactionBase): if not joining_date: frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) - for component_type in ("earnings", "deductions"): - for d in self.get(component_type): - d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] + for d in self.get(component_type): + d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] def set_loan_repayment(self): self.set('loans', []) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 848c3df57ab..f67a189ee71 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -25,7 +25,6 @@ class TestSalaryStructure(unittest.TestCase): make_employee("test_employee@salary.com") make_employee("test_employee_2@salary.com") - def make_holiday_list(self): if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): holiday_list = frappe.get_doc({ @@ -38,6 +37,29 @@ class TestSalaryStructure(unittest.TestCase): holiday_list.get_weekly_off_dates() holiday_list.save() + def test_salary_structure_deduction_based_on_gross_pay(self): + + emp = make_employee("test_employee_3@salary.com") + + sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True) + + sal_struct.earnings = [sal_struct.earnings[0]] + sal_struct.earnings[0].amount_based_on_formula = 1 + sal_struct.earnings[0].formula = "base" + + sal_struct.deductions = [sal_struct.deductions[0]] + + sal_struct.deductions[0].amount_based_on_formula = 1 + sal_struct.deductions[0].condition = "gross_pay > 100" + sal_struct.deductions[0].formula = "gross_pay * 0.2" + + sal_struct.submit() + + assignment = create_salary_structure_assignment(emp, "Salary Structure 2") + ss = make_salary_slip(sal_struct.name, employee = emp) + + self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) + def test_amount_totals(self): frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) From 756f47af04d57fd402f5f0874e04a850f28c4850 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:52:56 +0530 Subject: [PATCH 66/74] feat: Nested set filtering for accounting dimension (#20866) * feat: Nested set filtering for accounting dimension * fix: Remove print statement --- .../accounting_dimension/accounting_dimension.py | 14 +++++++++++++- .../accounts_receivable/accounts_receivable.py | 14 ++++++++------ erpnext/accounts/report/financial_statements.py | 13 ++++++++----- .../report/general_ledger/general_ledger.py | 11 +++++++---- .../report/sales_register/sales_register.py | 12 ++++++++---- .../accounts/report/trial_balance/trial_balance.py | 13 ++++++++----- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 522ed4ffa46..462d967d015 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -172,7 +172,7 @@ def get_doctypes_with_dimensions(): return doclist def get_accounting_dimensions(as_list=True): - accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled"]) + accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["label", "fieldname", "disabled", "document_type"]) if as_list: return [d.fieldname for d in accounting_dimensions] @@ -186,6 +186,18 @@ def get_checks_for_pl_and_bs_accounts(): return dimensions +def get_dimension_with_children(doctype, dimension): + + if isinstance(dimension, list): + dimension = dimension[0] + + all_dimensions = [] + lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) + children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + all_dimensions += [c.name for c in children] + + return all_dimensions + @frappe.whitelist() def get_dimension_filters(): dimension_filters = frappe.db.sql(""" diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0438f6d09e1..240b0d83817 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -7,7 +7,7 @@ from frappe import _, scrub from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds from collections import OrderedDict from erpnext.accounts.utils import get_currency_precision -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children # This report gives a summary of all Outstanding Invoices considering the following @@ -603,7 +603,6 @@ class ReceivablePayableReport(object): self.add_supplier_filters(conditions, values) self.add_accounting_dimensions_filters(conditions, values) - return " and ".join(conditions), values def get_order_by_condition(self): @@ -666,13 +665,16 @@ class ReceivablePayableReport(object): doctype=doctype, lft=lft, rgt=rgt, key=key) def add_accounting_dimensions_filters(self, conditions, values): - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: for dimension in accounting_dimensions: - if self.filters.get(dimension): - conditions.append("{0} = %s".format(dimension)) - values.append(self.filters.get(dimension)) + if self.filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + self.filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + self.filters.get(dimension.fieldname)) + conditions.append("{0} in %s".format(dimension.fieldname)) + values.append(tuple(self.filters.get(dimension.fieldname))) def get_gle_balance(self, gle): # get the balance of the GL (debit - credit) or reverse balance based on report type diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 75433a135f3..2636a1d2efc 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -16,7 +16,7 @@ from frappe import _ from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr) from six import itervalues -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, company=None, reset_period_on_fy_change=True): @@ -389,7 +389,7 @@ def set_gl_entries_by_account( def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if ignore_closing_entries: additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'") @@ -412,11 +412,14 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") - + if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - additional_conditions.append("{0} in (%({0})s)".format(dimension)) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + additional_conditions.append("{0} in %({0})s".format(dimension.fieldname)) return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 0939354efa7..f45911f90e5 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -10,7 +10,7 @@ from frappe import _, _dict from erpnext.accounts.utils import get_account_currency from erpnext.accounts.report.financial_statements import get_cost_centers_with_children from six import iteritems -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children from collections import OrderedDict def execute(filters=None): @@ -194,12 +194,15 @@ def get_conditions(filters): if match_conditions: conditions.append(match_conditions) - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - conditions.append("{0} in (%({0})s)".format(dimension)) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + conditions.append("{0} in %({0})s".format(dimension.fieldname)) return "and {}".format(" and ".join(conditions)) if conditions else "" diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 2be90bc5264..9864e40c1a9 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -6,7 +6,7 @@ import frappe from frappe.utils import flt from frappe import msgprint, _ from frappe.model.meta import get_field_precision -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -341,14 +341,18 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + conditions += """ and exists(select name from `tabSales Invoice Item` where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) + and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) return conditions diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 69285cc9634..5fe6b416fda 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.utils import flt, getdate, formatdate, cstr from erpnext.accounts.report.financial_statements \ import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit") @@ -109,7 +109,7 @@ def get_rootwise_opening_balances(filters, report_type): additional_conditions += fb_conditions - accounting_dimensions = get_accounting_dimensions() + accounting_dimensions = get_accounting_dimensions(as_list=False) query_filters = { "company": filters.company, @@ -122,11 +122,14 @@ def get_rootwise_opening_balances(filters, report_type): if accounting_dimensions: for dimension in accounting_dimensions: - if filters.get(dimension): - additional_conditions += """ and {0} in (%({0})s) """.format(dimension) + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + additional_conditions += "and {0} in %({0})s".format(dimension.fieldname) query_filters.update({ - dimension: filters.get(dimension) + dimension.fieldname: filters.get(dimension.fieldname) }) gle = frappe.db.sql(""" From 3ddcc2e5c77cda7cf82125e46d6fd0f54c58d26a Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 17 Mar 2020 10:56:13 +0530 Subject: [PATCH 67/74] fix: Item alternative must have similar fields as orignal item (#20799) * fix: Item alternative must have similar fields as orignal item * fix: Using db.get_values and meta instead of get_value and get_doc * fix: Made code more DRY Co-authored-by: Nabin Hait --- .../doctype/item_alternative/item_alternative.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py index 8e54539b18b..0c0d5b3271f 100644 --- a/erpnext/stock/doctype/item_alternative/item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/item_alternative.py @@ -22,6 +22,21 @@ class ItemAlternative(Document): if self.item_code == self.alternative_item_code: frappe.throw(_("Alternative item must not be same as item code")) + item_meta = frappe.get_meta("Item") + fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no","has_batch_no"] + item_data = frappe.db.get_values("Item", self.item_code, fields, as_dict=1) + alternative_item_data = frappe.db.get_values("Item", self.alternative_item_code, fields, as_dict=1) + + for field in fields: + if item_data[0].get(field) != alternative_item_data[0].get(field): + raise_exception, alert = [1, False] if field == "is_stock_item" else [0, True] + + frappe.msgprint(_("The value of {0} differs between Items {1} and {2}") \ + .format(frappe.bold(item_meta.get_label(field)), + frappe.bold(self.alternative_item_code), + frappe.bold(self.item_code)), + alert=alert, raise_exception=raise_exception) + def validate_duplicate(self): if frappe.db.get_value("Item Alternative", {'item_code': self.item_code, 'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}): From 7f58f6af3997fcd6d52460d107cb9082e70d3cba Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:58:17 +0530 Subject: [PATCH 68/74] fix: leave based on multiple holiday list (#20849) Co-authored-by: Nabin Hait --- .../leave_application/leave_application.py | 20 +++++++++++-------- .../leave_ledger_entry.json | 11 +++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 8c109464fdf..39e5e1132ca 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -366,7 +366,8 @@ class LeaveApplication(Document): leaves=self.total_leave_days * -1, from_date=self.from_date, to_date=self.to_date, - is_lwp=lwp + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee) ) create_leave_ledger_entry(self, args, submit) @@ -376,7 +377,9 @@ class LeaveApplication(Document): from_date=self.from_date, to_date=expiry_date, leaves=(date_diff(expiry_date, self.from_date) + 1) * -1, - is_lwp=lwp + is_lwp=lwp, + holiday_list=get_holiday_list_for_employee(self.employee), + ) create_leave_ledger_entry(self, args, submit) @@ -402,7 +405,7 @@ def get_allocation_expiry(employee, leave_type, to_date, from_date): return expiry[0]['to_date'] if expiry else None @frappe.whitelist() -def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): +def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None, holiday_list = None): number_of_days = 0 if cint(half_day) == 1: if from_date == to_date: @@ -416,7 +419,7 @@ def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day number_of_days = date_diff(to_date, from_date) + 1 if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"): - number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date)) + number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date, holiday_list=holiday_list)) return number_of_days @frappe.whitelist() @@ -567,7 +570,7 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): {'name': leave_entry.transaction_name}, ['half_day_date']) leave_days += get_number_of_leave_days(employee, leave_type, - leave_entry.from_date, leave_entry.to_date, half_day, half_day_date) * -1 + leave_entry.from_date, leave_entry.to_date, half_day, half_day_date, holiday_list=leave_entry.holiday_list) * -1 return leave_days @@ -580,7 +583,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date): ''' Returns leave entries between from_date and to_date ''' return frappe.db.sql(""" SELECT - employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, + employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s @@ -596,9 +599,10 @@ def get_leave_entries(employee, leave_type, from_date, to_date): }, as_dict=1) @frappe.whitelist() -def get_holidays(employee, from_date, to_date): +def get_holidays(employee, from_date, to_date, holiday_list = None): '''get holidays between two dates for the given employee''' - holiday_list = get_holiday_list_for_employee(employee) + if not holiday_list: + holiday_list = get_holiday_list_for_employee(employee) holidays = frappe.db.sql("""select count(distinct holiday_date) from `tabHoliday` h1, `tabHoliday List` h2 where h1.parent = h2.name and h1.holiday_date between %s and %s diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 771e706bbb3..a5ac3f3d471 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-09 15:47:39.760406", "doctype": "DocType", "engine": "InnoDB", @@ -12,6 +13,7 @@ "column_break_7", "from_date", "to_date", + "holiday_list", "is_carry_forward", "is_expired", "is_lwp", @@ -98,11 +100,18 @@ "fieldname": "is_lwp", "fieldtype": "Check", "label": "Is Leave Without Pay" + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" } ], "in_create": 1, "is_submittable": 1, - "modified": "2019-08-20 14:40:04.130799", + "links": [], + "modified": "2020-02-27 14:40:10.502605", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", From 6f454dce20fe1b78e106e6cdc8680dba732afbc7 Mon Sep 17 00:00:00 2001 From: Sun Howwrongbum Date: Tue, 17 Mar 2020 12:13:01 +0530 Subject: [PATCH 69/74] fix: null fields being set in Integration Request (#20894) Co-authored-by: Nabin Hait --- .../accounts/doctype/payment_request/payment_request.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1aff43c7177..6b74f477f2b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -126,12 +126,12 @@ class PaymentRequest(Document): return controller.get_payment_url(**{ "amount": flt(self.grand_total, self.precision("grand_total")), - "title": data.company.encode("utf-8"), - "description": self.subject.encode("utf-8"), + "title": frappe.as_unicode(data.company), + "description": frappe.as_unicode(self.subject), "reference_doctype": "Payment Request", "reference_docname": self.name, "payer_email": self.email_to or frappe.session.user, - "payer_name": frappe.safe_encode(data.customer_name), + "payer_name": frappe.as_unicode(data.customer_name), "order_id": self.name, "currency": self.currency }) @@ -475,4 +475,4 @@ def make_payment_order(source_name, target_doc=None): } }, target_doc, set_missing_values) - return doclist \ No newline at end of file + return doclist From 224059e7bac7479f41432e1b30a5326687c42360 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 17 Mar 2020 16:59:09 +0530 Subject: [PATCH 70/74] fix: Party Name not visible in statement of accounts (#20914) * fix: Party name in Statement Of Accounts * Update general_ledger.html Co-authored-by: Nabin Hait --- erpnext/accounts/report/general_ledger/general_ledger.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 40469aecc1d..9a2205a5791 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -2,7 +2,7 @@

{% if (filters.party_name) { %} {%= filters.party_name %} - {% } else if (filters.party && filters.show_name) { %} + {% } else if (filters.party) { %} {%= filters.party %} {% } else if (filters.account) { %} {%= filters.account %} From bc8a881e649e9ee7d0001e061e04ea59e86e6507 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 17 Mar 2020 17:00:24 +0530 Subject: [PATCH 71/74] fix: purchase return are allowed even when assets are not cancelled (#20798) * fix: purchase return are allowed even when assets are not cancelled * chore: test case * fix: error message Co-authored-by: Nabin Hait --- erpnext/controllers/buying_controller.py | 14 ++++++++++ .../purchase_receipt/test_purchase_receipt.py | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 75b896bb134..a47507eaf7b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -44,6 +44,7 @@ class BuyingController(StockController): self.validate_stock_or_nonstock_items() self.validate_warehouse() self.set_supplier_address() + self.validate_asset_return() if self.doctype=="Purchase Invoice": self.validate_purchase_receipt_if_update_stock() @@ -99,6 +100,19 @@ class BuyingController(StockController): for d in tax_for_valuation: d.category = 'Total' msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) + + def validate_asset_return(self): + if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return: + return + + purchase_doc_field = 'purchase_receipt' if self.doctype == 'Purchase Receipt' else 'purchase_invoice' + not_cancelled_asset = [d.name for d in frappe.db.get_all("Asset", { + purchase_doc_field: self.return_against, + "docstatus": 1 + })] + if self.is_return and len(not_cancelled_asset): + frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)), + title=_("Not Allowed")) def get_asset_items(self): if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6e19b71cac8..a51b25bf36d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -374,6 +374,33 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Asset', assets[0].name, 'location') self.assertEquals(location, "Test Location") + + def test_purchase_return_with_submitted_asset(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return + + pr = make_purchase_receipt(item_code="Test Asset Item", qty=1) + + asset = frappe.get_doc("Asset", { + 'purchase_receipt': pr.name + }) + asset.available_for_use_date = frappe.utils.nowdate() + asset.gross_purchase_amount = 50.0 + asset.append("finance_books", { + "expected_value_after_useful_life": 10, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 1, + "depreciation_start_date": frappe.utils.nowdate() + }) + asset.submit() + + pr_return = make_purchase_return(pr.name) + self.assertRaises(frappe.exceptions.ValidationError, pr_return.submit) + + asset.load_from_db() + asset.cancel() + + pr_return.submit() def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center From a6cd666b10a41566e450433be3ffac9abd0643b9 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 17 Mar 2020 17:00:41 +0530 Subject: [PATCH 72/74] fix: reserved qty for production issue for partial completion of work order (#20900) --- .../doctype/work_order/test_work_order.py | 34 ++++++++++++++++++- .../doctype/work_order/work_order.py | 6 ++-- erpnext/stock/doctype/bin/bin.py | 14 +++++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index ea2e7a96e1d..f917b098688 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -14,6 +14,7 @@ from erpnext.stock.utils import get_bin from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import make_item from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse class TestWorkOrder(unittest.TestCase): def setUp(self): @@ -82,6 +83,37 @@ class TestWorkOrder(unittest.TestCase): wo_order.set_work_order_operations() self.assertEqual(wo_order.planned_operating_cost, cost*2) + def test_resered_qty_for_partial_completion(self): + item = "_Test Item" + warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC") + + bin1_at_start = get_bin(item, warehouse) + + # reset to correct value + bin1_at_start.update_reserved_qty_for_production() + + wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, + source_warehouse=warehouse, skip_transfer=1) + + bin1_on_submit = get_bin(item, warehouse) + + # reserved qty for production is updated + self.assertEqual(cint(bin1_at_start.reserved_qty_for_production) + 2, + cint(bin1_on_submit.reserved_qty_for_production)) + + test_stock_entry.make_stock_entry(item_code="_Test Item", + target=warehouse, qty=100, basic_rate=100) + test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", + target=warehouse, qty=100, basic_rate=100) + + s = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1)) + s.submit() + + bin1_at_completion = get_bin(item, warehouse) + + self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production), + cint(bin1_on_submit.reserved_qty_for_production) - 1) + def test_production_item(self): wo_order = make_wo_order_test_record(item="_Test FG Item", qty=1, do_not_save=True) frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1") @@ -368,7 +400,7 @@ def make_wo_order_test_record(**args): wo_order.company = args.company or "_Test Company" wo_order.stock_uom = args.stock_uom or "_Test UOM" wo_order.use_multi_level_bom=0 - wo_order.skip_transfer=1 + wo_order.skip_transfer=args.skip_transfer or 0 wo_order.get_items_and_operations_from_bom() wo_order.sales_order = args.sales_order or None diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index b4dc3a55699..28bcc2e0f9c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -429,6 +429,9 @@ class WorkOrder(Document): update bin reserved_qty_for_production called from Stock Entry for production, after submit, cancel ''' + # calculate consumed qty based on submitted stock entries + self.update_consumed_qty_for_required_items() + if self.docstatus==1: # calculate transferred qty based on submitted stock entries self.update_transaferred_qty_for_required_items() @@ -436,9 +439,6 @@ class WorkOrder(Document): # update in bin self.update_reserved_qty_for_production() - # calculate consumed qty based on submitted stock entries - self.update_consumed_qty_for_required_items() - def update_reserved_qty_for_production(self, items=None): '''update reserved_qty_for_production in bins''' for d in self.required_items: diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 97a84726da8..73b36e3d852 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -69,15 +69,21 @@ class Bin(Document): '''Update qty reserved for production from Production Item tables in open work orders''' self.reserved_qty_for_production = frappe.db.sql(''' - select sum(item.required_qty - item.transferred_qty) - from `tabWork Order` pro, `tabWork Order Item` item - where + SELECT + CASE WHEN ifnull(skip_transfer, 0) = 0 THEN + SUM(item.required_qty - item.transferred_qty) + ELSE + SUM(item.required_qty - item.consumed_qty) + END + FROM `tabWork Order` pro, `tabWork Order Item` item + WHERE item.item_code = %s and item.parent = pro.name and pro.docstatus = 1 and item.source_warehouse = %s and pro.status not in ("Stopped", "Completed") - and item.required_qty > item.transferred_qty''', (self.item_code, self.warehouse))[0][0] + and (item.required_qty > item.transferred_qty or item.required_qty > item.consumed_qty) + ''', (self.item_code, self.warehouse))[0][0] self.set_projected_qty() From 738517c6e81218e89a4a00ade5cc0d6551d7658e Mon Sep 17 00:00:00 2001 From: Priyanka Gangar <59438065+0925-pinka@users.noreply.github.com> Date: Tue, 17 Mar 2020 19:39:06 +0530 Subject: [PATCH 73/74] Label changes in account settings hotfix (#20964) * fix: Account setting words switches to US Terminology * created united_states.js * Remove Unwanted Spaces * Remove all changes * Add spaces between arguments Co-authored-by: PriyankaGangar --- .../doctype/accounts_settings/regional/united_states.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 erpnext/accounts/doctype/accounts_settings/regional/united_states.js diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js new file mode 100644 index 00000000000..d47d6e58039 --- /dev/null +++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js @@ -0,0 +1,8 @@ + +frappe.ui.form.on('Accounts Settings', { + refresh: function(frm) { + frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through"); + frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods"); + frm.set_df_property("credit_controller", "label", "Credit Manager"); + } +}); \ No newline at end of file From 7cf245895e84604ec141a3d5c88455c515c345b7 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 2 Dec 2019 17:08:12 +0530 Subject: [PATCH 74/74] refactor: Report BOM Sock Calculated (#19431) --- .../bom_stock_calculated.py | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index be016ad9a47..f7b407b7922 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils.data import comma_and def execute(filters=None): # if not filters: filters = {} @@ -13,35 +14,36 @@ def execute(filters=None): data = get_bom_stock(filters) qty_to_make = filters.get("qty_to_make") + manufacture_details = get_manufacturer_records() for row in data: - item_map = get_item_details(row.item_code) reqd_qty = qty_to_make * row.actual_qty last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate") - if row.to_build > 0: - diff_qty = row.to_build - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price]) - else: - diff_qty = 0 - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price]) + summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details)) return columns, summ_data +def get_report_data(last_pur_price, reqd_qty, row, manufacture_details): + to_build = row.to_build if row.to_build > 0 else 0 + diff_qty = to_build - reqd_qty + return [row.item_code, row.description, + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False), + row.actual_qty, str(to_build), + reqd_qty, diff_qty, last_pur_price] + def get_columns(): """return columns""" columns = [ _("Item") + ":Link/Item:100", _("Description") + "::150", - _("Manufacturer") + "::100", - _("Manufacturer Part Number") + "::100", + _("Manufacturer") + "::250", + _("Manufacturer Part Number") + "::250", _("Qty") + ":Float:50", _("Stock Qty") + ":Float:100", _("Reqd Qty")+ ":Float:100", _("Diff Qty")+ ":Float:100", _("Last Purchase Price")+ ":Float:100", - - ] - return columns def get_bom_stock(filters): @@ -85,7 +87,12 @@ def get_bom_stock(filters): GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) -def get_item_details(item_code): - items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1) +def get_manufacturer_records(): + details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"]) + manufacture_details = frappe._dict() + for detail in details: + dic = manufacture_details.setdefault(detail.get('parent'), {}) + dic.setdefault('manufacturer', []).append(detail.get('manufacturer')) + dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no')) - return dict((d.name, d) for d in items) + return manufacture_details \ No newline at end of file