From 3441d123b68497a90e5c4b6a9669c76f01a63f6f Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:36:06 +0530 Subject: [PATCH 001/410] feat: additional customer fileds add customer_group, price list, currency etc. rearrange and group fields disable cusotmer edit after save (temp fix) --- erpnext/healthcare/doctype/patient/patient.js | 1 + .../healthcare/doctype/patient/patient.json | 81 +++++++++++++------ erpnext/healthcare/doctype/patient/patient.py | 10 ++- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index d5df9567ec5..a11faf58066 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -40,6 +40,7 @@ frappe.ui.form.on('Patient', { frm.add_custom_button(__('Patient Encounter'), function () { create_encounter(frm); }, 'Create'); + frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option } }, onload: function (frm) { diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 4258e4011de..2c701fbf944 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -24,13 +24,20 @@ "image", "column_break_14", "status", - "inpatient_status", "inpatient_record", - "customer", + "inpatient_status", + "report_preference", "mobile", "email", "phone", - "report_preference", + "customer_details_section", + "customer", + "customer_group", + "territory", + "column_break_24", + "default_currency", + "default_price_list", + "language", "personal_and_social_history", "occupation", "column_break_25", @@ -52,9 +59,7 @@ "surrounding_factors", "other_risk_factors", "more_info", - "patient_details", - "ac_sb", - "default_currency" + "patient_details" ], "fields": [ { @@ -156,6 +161,7 @@ "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_preview": 1, "label": "Customer", "options": "Customer" }, @@ -171,7 +177,8 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Mobile" + "label": "Mobile", + "options": "Phone" }, { "bold": 1, @@ -186,7 +193,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "label": "Phone" + "label": "Phone", + "options": "Phone" }, { "collapsible": 1, @@ -268,25 +276,25 @@ "fieldname": "tobacco_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Past)" + "label": "Tobacco Consumption (Past)" }, { "fieldname": "tobacco_current_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Present)" + "label": "Tobacco Consumption (Present)" }, { "fieldname": "alcohol_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Alcohol Consumption Habbits (Past)" + "label": "Alcohol Consumption (Past)" }, { "fieldname": "alcohol_current_use", "fieldtype": "Data", "ignore_user_permissions": 1, - "label": "Alcohol Consumption Habbits (Present)" + "label": "Alcohol Consumption (Present)" }, { "fieldname": "column_break_32", @@ -320,20 +328,11 @@ "ignore_xss_filter": 1, "label": "Patient Details" }, - { - "collapsible": 1, - "fieldname": "ac_sb", - "fieldtype": "Section Break", - "label": "Account Details" - }, { "fieldname": "default_currency", "fieldtype": "Link", - "hidden": 1, - "ignore_xss_filter": 1, - "label": "Default Currency", - "options": "Currency", - "print_hide": 1 + "label": "Billing Currency", + "options": "Currency" }, { "fieldname": "last_name", @@ -351,13 +350,47 @@ "fieldname": "middle_name", "fieldtype": "Data", "label": "Middle Name (optional)" + }, + { + "collapsible": 1, + "fieldname": "customer_details_section", + "fieldtype": "Section Break", + "label": "Customer Details" + }, + { + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_price_list", + "fieldtype": "Link", + "label": "Default Price List", + "options": "Price List" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "image_field": "image", "links": [], "max_attachments": 50, - "modified": "2020-04-06 12:55:30.807744", + "modified": "2020-04-11 14:53:48.767245", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e304a0bbc36..f83fac03a63 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -26,6 +26,7 @@ class Patient(Document): frappe.db.set_value('Patient', self.name, 'status', 'Disabled') else: send_registration_sms(self) + self.reload() # self.notify_update() def set_full_name(self): if self.last_name: @@ -86,8 +87,8 @@ class Patient(Document): return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') - territory = frappe.db.get_single_value('Selling Settings', 'territory') + customer_group = doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group') + territory = doc.territory or frappe.db.get_single_value('Selling Settings', 'territory') if not (customer_group and territory): customer_group = get_root_of('Customer Group') territory = get_root_of('Territory') @@ -98,7 +99,10 @@ def create_customer(doc): 'customer_name': doc.patient_name, 'customer_group': customer_group, 'territory' : territory, - 'customer_type': 'Individual' + 'customer_type': 'Individual', + 'default_currency': doc.default_currency, + 'default_price_ist': doc.default_price_list, + 'language': doc.language }).insert(ignore_permissions=True, ignore_mandatory=True) frappe.db.set_value('Patient', doc.name, 'customer', customer.name) From 9a6bf4e0c97c1b28ae663ab26f5363d25c6bedde Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:56:09 +0530 Subject: [PATCH 002/410] fix: vitals added naimg series, minor field rearrangements --- .../healthcare_practitioner.json | 10 +++++++--- .../sample_collection/sample_collection.json | 8 ++++---- .../doctype/vital_signs/vital_signs.json | 20 ++++++++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index fd5b6e12f66..cb747f95ef8 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_copy": 1, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -51,17 +50,20 @@ "fieldname": "first_name", "fieldtype": "Data", "label": "First Name", + "no_copy": 1, "reqd": 1 }, { "fieldname": "middle_name", "fieldtype": "Data", - "label": "Middle Name (Optional)" + "label": "Middle Name (Optional)", + "no_copy": 1 }, { "fieldname": "last_name", "fieldtype": "Data", - "label": "Last Name" + "label": "Last Name", + "no_copy": 1 }, { "fieldname": "image", @@ -226,6 +228,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Full Name", + "no_copy": 1, "read_only": 1, "search_index": 1 }, @@ -233,6 +236,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "HLC-PRAC-.YYYY.-", "report_hide": 1, "set_only_once": 1 diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 39cead88621..c352287faf4 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -9,14 +9,14 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", - "column_break_4", "patient_age", "patient_sex", + "column_break_4", + "inpatient_record", "company", + "invoiced", "section_break_6", "sample", "sample_uom", @@ -167,7 +167,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-25 16:55:52.376834", + "modified": "2020-04-04 19:17:02.707203", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 75726dbe070..fdacda6277d 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -8,11 +8,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "patient", "patient_name", - "appointment", - "encounter", "column_break_2", "signs_date", "signs_time", @@ -34,6 +31,11 @@ "bmi", "column_break_14", "nutrition_note", + "sb_references", + "inpatient_record", + "appointment", + "encounter", + "column_break_28", "company", "amended_from" ], @@ -217,7 +219,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -229,11 +230,20 @@ "options": "Vital Signs", "print_hide": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "sb_references", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-04 17:19:29.549889", + "modified": "2020-04-03 23:06:29.786184", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", From 174dcee155c033fcd01ffa5d93e8cf4f5a20e767 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:09:06 +0530 Subject: [PATCH 003/410] fix: set company while creating encounter, fields rearranged --- .../patient_appointment.js | 3 +- .../patient_appointment.json | 43 +++++++++++++------ .../patient_appointment.py | 3 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index efa6b249b31..de8dc0e90ae 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -193,7 +193,6 @@ let check_and_set_availability = function(frm) { d.hide(); frm.enable_save(); frm.save(); - frm.enable_save(); d.get_primary_btn().attr('disabled', true); } }); @@ -400,6 +399,7 @@ let create_vital_signs = function(frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -432,6 +432,7 @@ frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { callback: function (data) { frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 7f9a671d47e..81f75975636 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -14,13 +14,16 @@ "patient_name", "patient_sex", "patient_age", - "inpatient_record", "column_break_1", - "status", - "procedure_template", - "get_procedure_from_encounter", - "procedure_prescription", + "inpatient_record", + "company", "service_unit", + "status", + "section_break_11", + "get_procedure_from_encounter", + "column_break_13", + "procedure_template", + "procedure_prescription", "section_break_12", "practitioner", "department", @@ -32,9 +35,9 @@ "duration", "section_break_16", "mode_of_payment", - "paid_amount", - "company", + "billing_item", "column_break_2", + "paid_amount", "invoiced", "ref_sales_invoice", "section_break_3", @@ -114,7 +117,8 @@ "label": "Procedure Prescription", "no_copy": 1, "options": "Procedure Prescription", - "print_hide": 1 + "print_hide": 1, + "read_only": 1 }, { "fieldname": "service_unit", @@ -140,6 +144,7 @@ "set_only_once": 1 }, { + "fetch_from": "practitioner.department", "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -234,12 +239,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "no_copy": 1, - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "collapsible": 1, @@ -282,10 +284,25 @@ "label": "Series", "options": "HLC-APP-.YYYY.-", "set_only_once": 1 + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_item", + "fieldtype": "Link", + "label": "Billing Item", + "options": "Item", + "read_only": 1 } ], "links": [], - "modified": "2020-03-27 11:27:33.773195", + "modified": "2020-04-07 11:16:34.981240", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a2d9d0240f2..3786bf28619 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -340,7 +340,8 @@ def make_encounter(source_name, target_doc=None): ['medical_department', 'department'], ['patient_sex', 'patient_sex'], ['encounter_date', 'appointment_date'], - ['invoiced', 'invoiced'] + ['invoiced', 'invoiced'], + ['company', 'company'] ] } }, target_doc) From d2224626c7c94c5a579606091efbea1ab52b591b Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:11:59 +0530 Subject: [PATCH 004/410] fix: company when creatign procedure, practitioner name added --- .../patient_encounter/patient_encounter.js | 9 ++++--- .../patient_encounter/patient_encounter.json | 24 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 83c5d2be9c0..0e34164d07d 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -110,7 +110,8 @@ frappe.ui.form.on('Patient Encounter', { 'patient':data.message.patient, 'type': data.message.appointment_type, 'practitioner': data.message.practitioner, - 'invoiced': data.message.invoiced + 'invoiced': data.message.invoiced, + 'company': data.message.company }; frm.set_value(values); } @@ -209,7 +210,8 @@ let create_vital_signs = function (frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.appointment, - 'encounter': frm.doc.name + 'encounter': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -220,7 +222,8 @@ let create_procedure = function (frm) { } frappe.route_options = { 'patient': frm.doc.patient, - 'medical_department': frm.doc.medical_department + 'medical_department': frm.doc.medical_department, + 'company': frm.doc.company }; frappe.new_doc('Clinical Procedure'); }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d00e7bc7dd2..935935e0c01 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -17,9 +17,9 @@ "patient_name", "patient_sex", "patient_age", - "company", "column_break_6", "practitioner", + "practitioner_name", "medical_department", "encounter_date", "encounter_time", @@ -43,6 +43,8 @@ "sb_procedures", "procedure_prescription", "encounter_comment", + "sb_refs", + "company", "amended_from" ], "fields": [ @@ -80,7 +82,6 @@ "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", "options": "Patient", @@ -110,7 +111,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -121,7 +121,6 @@ { "fieldname": "practitioner", "fieldtype": "Link", - "in_list_view": 1, "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", @@ -272,7 +271,6 @@ "fieldname": "medical_department", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Department", "options": "Medical Department", @@ -287,11 +285,23 @@ { "fieldname": "column_break_17", "fieldtype": "Column Break" + }, + { + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-27 12:42:21.751964", + "modified": "2020-04-03 23:06:16.348846", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", @@ -318,7 +328,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "patient_name", "track_changes": 1, "track_seen": 1 } \ No newline at end of file From 5f923fce2de89abfb3f517573c190e5ef071aaff Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:30:00 +0530 Subject: [PATCH 005/410] feat: multi company support --- .../healthcare/doctype/lab_test/lab_test.js | 9 ++++---- .../healthcare/doctype/lab_test/lab_test.json | 15 ++++++------- .../healthcare/doctype/lab_test/lab_test.py | 21 +++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 5b3f4c705ad..e7e44dcb481 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -137,7 +137,7 @@ var get_lab_test_prescribed = function(frm){ }); } else{ - frappe.msgprint(__("Please select Patient to get Lab Tests")); + frappe.msgprint(__("Please select a Patient to get Lab Orders")); } }; @@ -180,9 +180,10 @@ var show_lab_tests = function(frm, result){ return false; }); }); - if(!result){ - var msg = "There are no Lab Test prescribed for "+frm.doc.patient; - $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); + if(!result.length){ + var msg = "No Lab Orders found for patient " + frm.doc.patient_name + ""; + html_field.empty(); + $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } d.show(); }; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index ccbc24b3fb6..17dc1edd8c1 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -9,18 +9,18 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", "patient_name", "patient_age", "patient_sex", - "practitioner", + "report_preference", "email", "mobile", - "company", + "practitioner", "c_b", + "inpatient_record", + "company", "department", "status", "submitted_date", @@ -31,7 +31,7 @@ "employee_name", "employee_designation", "user", - "report_preference", + "invoiced", "sb_first", "lab_test_name", "column_break_26", @@ -153,7 +153,7 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", "options": "Company", "print_hide": 1, @@ -168,6 +168,7 @@ "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_standard_filter": 1, "label": "Department", "options": "Medical Department", "search_index": 1 @@ -427,7 +428,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-23 19:37:06.617764", + "modified": "2020-04-04 19:16:29.131168", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 4e4015d2f09..e49429ea625 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -69,9 +69,9 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created.")) + frappe.msgprint(_("Lab Test(s) " + lab_test_created + " created.")) else: - frappe.msgprint(_("No Lab Test created")) + frappe.msgprint(_("No Lab Tests created")) def create_lab_test_from_encounter(encounter_id): lab_test_created = False @@ -87,7 +87,7 @@ def create_lab_test_from_encounter(encounter_id): for lab_test_id in lab_test_ids: template = get_lab_test_template(lab_test_id[1]) if template: - lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template) + lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template, encounter.company) lab_test.save(ignore_permissions = True) frappe.db.set_value("Lab Prescription", lab_test_id[0], "lab_test_created", 1) if not lab_test_created: @@ -111,7 +111,7 @@ def create_lab_test_from_invoice(invoice_name): if lab_test_created != 1: template = get_lab_test_template(item.item_code) if template: - lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, company, invoice.company) if item.reference_dt == "Lab Prescription": lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) @@ -121,7 +121,7 @@ def create_lab_test_from_invoice(invoice_name): if not lab_tests_created: lab_tests_created = lab_test.name else: - lab_tests_created += ", "+lab_test.name + lab_tests_created += ", " + lab_test.name return lab_tests_created def get_lab_test_template(item): @@ -141,7 +141,7 @@ def check_template_exists(item): return template_exists return False -def create_lab_test_doc(invoiced, practitioner, patient, template): +def create_lab_test_doc(invoiced, practitioner, patient, template, company): lab_test = frappe.new_doc("Lab Test") lab_test.invoiced = invoiced lab_test.practitioner = practitioner @@ -150,11 +150,12 @@ def create_lab_test_doc(invoiced, practitioner, patient, template): lab_test.patient_sex = patient.sex lab_test.email = patient.email lab_test.mobile = patient.mobile + lab_test.report_preference = patient.report_preference lab_test.department = template.department lab_test.template = template.name lab_test.lab_test_group = template.lab_test_group lab_test.result_date = getdate() - lab_test.report_preference = patient.report_preference + lab_test.company = company return lab_test def create_normals(template, lab_test): @@ -190,7 +191,7 @@ def create_specials(template, lab_test): special.require_result_value = 1 special.template = template.name -def create_sample_doc(template, patient, invoice): +def create_sample_doc(template, patient, invoice, company = None): if template.sample: sample_exists = frappe.db.exists({ "doctype": "Sample Collection", @@ -221,6 +222,8 @@ def create_sample_doc(template, patient, invoice): sample_collection.sample = template.sample sample_collection.sample_uom = template.sample_uom sample_collection.sample_qty = template.sample_qty + sample_collection.company = company + if(template.sample_details): sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details sample_collection.save(ignore_permissions=True) @@ -229,7 +232,7 @@ def create_sample_doc(template, patient, invoice): def create_sample_collection(lab_test, template, patient, invoice): if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"): - sample_collection = create_sample_doc(template, patient, invoice) + sample_collection = create_sample_doc(template, patient, invoice, lab_test.company) if(sample_collection): lab_test.sample = sample_collection.name return lab_test From 0686c3d729ab8046c1a79b233bb6b7d9ece78ba7 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:32:35 +0530 Subject: [PATCH 006/410] feat: multi company support --- .../clinical_procedure/clinical_procedure.js | 35 ++++++++--- .../clinical_procedure.json | 58 ++++++++++++++++--- .../clinical_procedure/clinical_procedure.py | 2 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 5f36bdd95c6..213fa3de1f5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -175,16 +175,37 @@ frappe.ui.form.on('Clinical Procedure', { name: frm.doc.appointment }, callback: function(data) { - frm.set_value('patient', data.message.patient); - frm.set_value('procedure_template', data.message.procedure_template); - frm.set_value('medical_department', data.message.department); - frm.set_value('start_date', data.message.appointment_date); - frm.set_value('start_time', data.message.appointment_time); - frm.set_value('notes', data.message.notes); - frm.set_value('service_unit', data.message.service_unit); + let values = { + 'patient':data.message.patient, + 'procedure_template': data.message.procedure_template, + 'medical_department': data.message.department, + 'start_date': data.message.appointment_date, + 'start_time': data.message.appointment_time, + 'notes': data.message.notes, + 'service_unit': data.message.service_unit, + 'company': data.message.company + } + frm.set_value(values); } }); } + else{ + let values = { + 'patient': '', + 'patient_name': '', + 'patient_sex': '', + 'patient_age': '', + 'medical_department': '', + 'procedure_template': '', + 'start_date': '', + 'start_time': '', + 'notes': '', + 'service_unit': '', + 'inpatient_record': '' + // 'inpatient_status': '' + }; + frm.set_value(values); + } }, procedure_template: function(frm) { diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 3c936bbf277..59a87eccde4 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -7,16 +7,18 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "procedure_template", "appointment", + "column_break_30", + "procedure_template", + "section_break_6", "patient", + "patient_name", "patient_sex", "patient_age", - "prescription", "medical_department", "practitioner", + "practitioner_name", "column_break_7", "status", "service_unit", @@ -26,7 +28,6 @@ "sample", "invoiced", "notes", - "company", "consumables_section", "consume_stock", "items", @@ -36,6 +37,11 @@ "consumable_total_amount", "column_break_27", "consumption_details", + "sb_refs", + "inpatient_record", + "company", + "column_break_34", + "prescription", "amended_from" ], "fields": [ @@ -56,7 +62,7 @@ { "fieldname": "appointment", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Appointment", "options": "Patient Appointment" }, @@ -64,7 +70,7 @@ "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Patient", "options": "Patient", "reqd": 1 @@ -88,17 +94,20 @@ "fieldtype": "Link", "hidden": 1, "label": "Procedure Prescription", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "read_only": 1 }, { "fieldname": "medical_department", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Medical Department", "options": "Medical Department" }, { "fieldname": "practitioner", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner" }, @@ -237,11 +246,43 @@ { "fieldname": "section_break_24", "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-02 11:44:27.970651", + "modified": "2020-04-03 23:06:04.009856", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -263,5 +304,6 @@ "restrict_to_domain": "Healthcare", "sort_field": "modified", "sort_order": "DESC", + "title_field": "patient_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index db3afc8807e..56617e52586 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -37,7 +37,7 @@ class ClinicalProcedure(Document): template = frappe.get_doc('Clinical Procedure Template', self.procedure_template) if template.sample: patient = frappe.get_doc('Patient', self.patient) - sample_collection = create_sample_doc(template, patient, None) + sample_collection = create_sample_doc(template, patient, None, self.company) frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name) self.reload() From f90ea8d622c6ffeed1b8d9c02d2cc166f0fc236e Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:34:28 +0530 Subject: [PATCH 007/410] company field added --- .../inpatient_record/inpatient_record.json | 1165 ++++------------- 1 file changed, 220 insertions(+), 945 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index 92c11fbb4da..c1b516d5363 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -1,980 +1,255 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-07-11 17:48:51.404139", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2018-07-11 17:48:51.404139", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "naming_series", + "patient", + "patient_name", + "gender", + "blood_group", + "dob", + "mobile", + "email", + "phone", + "column_break_8", + "company", + "status", + "scheduled_date", + "admitted_datetime", + "expected_discharge", + "discharge_date", + "references", + "cb_admission", + "admission_practitioner", + "admission_encounter", + "cb_discharge", + "discharge_practitioner", + "discharge_encounter", + "sb_inpatient_occupancy", + "inpatient_occupancies", + "btn_transfer", + "sb_discharge_note", + "discharge_note" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, { - "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": 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": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-INP-.YYYY.-", - "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": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "HLC-INP-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "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": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "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 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.sex", - "fieldname": "gender", - "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "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 - }, + "fetch_from": "patient.sex", + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.blood_group", - "fieldname": "blood_group", - "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": "Blood Group", - "length": 0, - "no_copy": 0, - "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", - "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 - }, + "fetch_from": "patient.blood_group", + "fieldname": "blood_group", + "fieldtype": "Select", + "label": "Blood Group", + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dob", - "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 of birth", - "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 - }, + "fetch_from": "patient.dob", + "fieldname": "dob", + "fieldtype": "Date", + "label": "Date of birth", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.mobile", - "fieldname": "mobile", - "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": "Mobile", - "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 - }, + "fetch_from": "patient.mobile", + "fieldname": "mobile", + "fieldtype": "Data", + "label": "Mobile", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.email", - "fieldname": "email", - "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": "Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "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 - }, + "fetch_from": "patient.email", + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.phone", - "fieldname": "phone", - "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": "Phone", - "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 - }, + "fetch_from": "patient.phone", + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "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_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "scheduled_date", - "fieldtype": "Date", - "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": "Admission Schedule 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 - }, + "default": "Today", + "fieldname": "scheduled_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Admission Schedule Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "admitted_datetime", - "fieldtype": "Datetime", - "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": "Admitted Datetime", - "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 - }, + "default": "Today", + "fieldname": "admitted_datetime", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Admitted Datetime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expected_discharge", - "fieldtype": "Date", - "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": "Expected Discharge", - "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": "expected_discharge", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expected Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_date", - "fieldtype": "Date", - "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": "Discharge 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "references", - "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": "References", - "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": "references", + "fieldtype": "Section Break", + "label": "References" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_admission", - "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, - "label": "Admission", - "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": "cb_admission", + "fieldtype": "Column Break", + "label": "Admission" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_discharge", - "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, - "label": "Discharge", - "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": "cb_discharge", + "fieldtype": "Column Break", + "label": "Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_inpatient_occupancy", - "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": "Inpatient Occupancy", - "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": "sb_inpatient_occupancy", + "fieldtype": "Section Break", + "label": "Inpatient Occupancy" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inpatient_occupancies", - "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, - "length": 0, - "no_copy": 0, - "options": "Inpatient Occupancy", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_occupancies", + "fieldtype": "Table", + "options": "Inpatient Occupancy", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "btn_transfer", - "fieldtype": "Button", - "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": "Transfer", - "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": "btn_transfer", + "fieldtype": "Button", + "label": "Transfer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.status != \"Admission Scheduled\"", - "fieldname": "sb_discharge_note", - "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": "Discharge Note", - "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 - }, + "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "fieldname": "sb_discharge_note", + "fieldtype": "Section Break", + "label": "Discharge Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_note", - "fieldtype": "Text Editor", - "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": "", - "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": "discharge_note", + "fieldtype": "Text Editor" + }, + { + "fetch_from": "admission_encounter.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:43.168245", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Inpatient Record", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-04-07 13:13:39.351977", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Inpatient Record", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1 +} \ No newline at end of file From c01fb2a4d8752b663828513fe722d4d62414b0da Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:42:03 +0530 Subject: [PATCH 008/410] feat: multi-company billing sales onvoice - filter get items based on company utils - company filters in all get item helper methods utils - refactor appointemnt items --- .../doctype/sales_invoice/sales_invoice.js | 7 +- erpnext/healthcare/utils.py | 96 ++++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 6f78db2ccc3..7741842fca1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -919,7 +919,7 @@ var get_healthcare_services_to_invoice = function(frm) { if(patient && patient!=selected_patient){ selected_patient = patient; var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; - var args = {patient: patient}; + var args = {patient: patient, company: frm.doc.company}; var columns = (["service", "reference_name", "reference_type"]); get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); } @@ -1063,7 +1063,10 @@ var get_drugs_to_invoice = function(frm) { description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', get_query: function(doc) { return { - filters: { patient: dialog.get_value("patient"), docstatus: 1 } + filters: { patient: dialog.get_value("patient"), + company: frm.doc.company, + docstatus: 1 + } }; } }, diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 246242ad84c..e9cc597e813 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -3,82 +3,84 @@ # For license information, please see license.txt from __future__ import unicode_literals +import math +import json import frappe from frappe import _ -import math from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() -def get_healthcare_services_to_invoice(patient): +def get_healthcare_services_to_invoice(patient, company): patient = frappe.get_doc('Patient', patient) + items_to_invoice = [] if patient: validate_customer_created(patient) - items_to_invoice = [] - patient_appointments = frappe.get_list( - 'Patient Appointment', - fields='*', - filters={'patient': patient.name, 'invoiced': 0}, - order_by='appointment_date' - ) - if patient_appointments: - items_to_invoice = get_fee_validity(patient_appointments) + # Customer validated, build a list of billable services + items_to_invoice += get_appointments_to_invoice(patient, company) + items_to_invoice += get_encounters_to_invoice(patient, company) + items_to_invoice += get_lab_tests_to_invoice(patient, company) + items_to_invoice += get_clinical_procedures_to_invoice(patient, company) + items_to_invoice += get_inpatient_services_to_invoice(patient, company) - encounters = get_encounters_to_invoice(patient) - lab_tests = get_lab_tests_to_invoice(patient) - clinical_procedures = get_clinical_procedures_to_invoice(patient) - inpatient_services = get_inpatient_services_to_invoice(patient) - - items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services return items_to_invoice + def validate_customer_created(patient): if not frappe.db.get_value('Patient', patient.name, 'customer'): msg = _("Please set a Customer linked to the Patient") msg += " {0}".format(patient.name) frappe.throw(msg, title=_('Customer Not Found')) -def get_fee_validity(patient_appointments): - if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): - return - items_to_invoice = [] +def get_appointments_to_invoice(patient, company): + appointments_to_invoice = [] + patient_appointments = frappe.get_list( + 'Patient Appointment', + fields = '*', + filters = {'patient': patient.name, 'company': company, 'invoiced': 0}, + order_by = 'appointment_date' + ) + for appointment in patient_appointments: + # Procedure Appointments if appointment.procedure_template: if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): - items_to_invoice.append({ + appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', 'reference_name': appointment.name, 'service': appointment.procedure_template }) + # Consultation Appointments, should check fee validity else: - fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}) - if not fee_validity: - practitioner_charge = 0 - income_account = None - service_item = None - if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) - income_account = get_income_account(appointment.practitioner, appointment.company) - items_to_invoice.append({ - 'reference_type': 'Patient Appointment', - 'reference_name': appointment.name, - 'service': service_item, - 'rate': practitioner_charge, - 'income_account': income_account - }) + if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \ + frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}): + continue # Skip invoicing, fee validty present + practitioner_charge = 0 + income_account = None + service_item = None + if appointment.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + income_account = get_income_account(appointment.practitioner, appointment.company) + appointments_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) - return items_to_invoice + return appointments_to_invoice -def get_encounters_to_invoice(patient): +def get_encounters_to_invoice(patient, company): encounters_to_invoice = [] encounters = frappe.get_list( 'Patient Encounter', fields=['*'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) if encounters: for encounter in encounters: @@ -101,12 +103,12 @@ def get_encounters_to_invoice(patient): return encounters_to_invoice -def get_lab_tests_to_invoice(patient): +def get_lab_tests_to_invoice(patient, company): lab_tests_to_invoice = [] lab_tests = frappe.get_list( 'Lab Test', fields=['name', 'template'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) for lab_test in lab_tests: item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable']) @@ -142,12 +144,12 @@ def get_lab_tests_to_invoice(patient): return lab_tests_to_invoice -def get_clinical_procedures_to_invoice(patient): +def get_clinical_procedures_to_invoice(patient, company): clinical_procedures_to_invoice = [] procedures = frappe.get_list( 'Clinical Procedure', fields='*', - filters={'patient': patient.name, 'invoiced': False} + filters={'patient': patient.name, 'company': company, 'invoiced': False} ) for procedure in procedures: if not procedure.appointment: @@ -203,7 +205,7 @@ def get_clinical_procedures_to_invoice(patient): return clinical_procedures_to_invoice -def get_inpatient_services_to_invoice(patient): +def get_inpatient_services_to_invoice(patient, company): services_to_invoice = [] inpatient_services = frappe.db.sql( ''' @@ -213,10 +215,11 @@ def get_inpatient_services_to_invoice(patient): `tabInpatient Record` ip, `tabInpatient Occupancy` io WHERE ip.patient=%s + and ip.company=%s and io.parent=ip.name and io.left=1 and io.invoiced=0 - ''', (patient.name), as_dict=1) + ''', (patient.name, company), as_dict=1) for inpatient_occupancy in inpatient_services: service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type') @@ -376,6 +379,7 @@ def check_fee_validity(appointment): def manage_fee_validity(appointment): fee_validity = check_fee_validity(appointment) + if fee_validity: if appointment.status == 'Cancelled' and fee_validity.visited > 0: fee_validity.visited -= 1 From 407b33d7e09be346d05ec938dbb3d10f8a46d93f Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:36:06 +0530 Subject: [PATCH 009/410] feat: additional customer fileds add customer_group, price list, currency etc. rearrange and group fields disable cusotmer edit after save (temp fix) --- erpnext/healthcare/doctype/patient/patient.js | 1 + .../healthcare/doctype/patient/patient.json | 81 +++++++++++++------ erpnext/healthcare/doctype/patient/patient.py | 10 ++- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index d5df9567ec5..a11faf58066 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -40,6 +40,7 @@ frappe.ui.form.on('Patient', { frm.add_custom_button(__('Patient Encounter'), function () { create_encounter(frm); }, 'Create'); + frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option } }, onload: function (frm) { diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 4258e4011de..2c701fbf944 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -24,13 +24,20 @@ "image", "column_break_14", "status", - "inpatient_status", "inpatient_record", - "customer", + "inpatient_status", + "report_preference", "mobile", "email", "phone", - "report_preference", + "customer_details_section", + "customer", + "customer_group", + "territory", + "column_break_24", + "default_currency", + "default_price_list", + "language", "personal_and_social_history", "occupation", "column_break_25", @@ -52,9 +59,7 @@ "surrounding_factors", "other_risk_factors", "more_info", - "patient_details", - "ac_sb", - "default_currency" + "patient_details" ], "fields": [ { @@ -156,6 +161,7 @@ "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_preview": 1, "label": "Customer", "options": "Customer" }, @@ -171,7 +177,8 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Mobile" + "label": "Mobile", + "options": "Phone" }, { "bold": 1, @@ -186,7 +193,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "label": "Phone" + "label": "Phone", + "options": "Phone" }, { "collapsible": 1, @@ -268,25 +276,25 @@ "fieldname": "tobacco_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Past)" + "label": "Tobacco Consumption (Past)" }, { "fieldname": "tobacco_current_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Present)" + "label": "Tobacco Consumption (Present)" }, { "fieldname": "alcohol_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Alcohol Consumption Habbits (Past)" + "label": "Alcohol Consumption (Past)" }, { "fieldname": "alcohol_current_use", "fieldtype": "Data", "ignore_user_permissions": 1, - "label": "Alcohol Consumption Habbits (Present)" + "label": "Alcohol Consumption (Present)" }, { "fieldname": "column_break_32", @@ -320,20 +328,11 @@ "ignore_xss_filter": 1, "label": "Patient Details" }, - { - "collapsible": 1, - "fieldname": "ac_sb", - "fieldtype": "Section Break", - "label": "Account Details" - }, { "fieldname": "default_currency", "fieldtype": "Link", - "hidden": 1, - "ignore_xss_filter": 1, - "label": "Default Currency", - "options": "Currency", - "print_hide": 1 + "label": "Billing Currency", + "options": "Currency" }, { "fieldname": "last_name", @@ -351,13 +350,47 @@ "fieldname": "middle_name", "fieldtype": "Data", "label": "Middle Name (optional)" + }, + { + "collapsible": 1, + "fieldname": "customer_details_section", + "fieldtype": "Section Break", + "label": "Customer Details" + }, + { + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_price_list", + "fieldtype": "Link", + "label": "Default Price List", + "options": "Price List" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "image_field": "image", "links": [], "max_attachments": 50, - "modified": "2020-04-06 12:55:30.807744", + "modified": "2020-04-11 14:53:48.767245", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e304a0bbc36..f83fac03a63 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -26,6 +26,7 @@ class Patient(Document): frappe.db.set_value('Patient', self.name, 'status', 'Disabled') else: send_registration_sms(self) + self.reload() # self.notify_update() def set_full_name(self): if self.last_name: @@ -86,8 +87,8 @@ class Patient(Document): return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') - territory = frappe.db.get_single_value('Selling Settings', 'territory') + customer_group = doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group') + territory = doc.territory or frappe.db.get_single_value('Selling Settings', 'territory') if not (customer_group and territory): customer_group = get_root_of('Customer Group') territory = get_root_of('Territory') @@ -98,7 +99,10 @@ def create_customer(doc): 'customer_name': doc.patient_name, 'customer_group': customer_group, 'territory' : territory, - 'customer_type': 'Individual' + 'customer_type': 'Individual', + 'default_currency': doc.default_currency, + 'default_price_ist': doc.default_price_list, + 'language': doc.language }).insert(ignore_permissions=True, ignore_mandatory=True) frappe.db.set_value('Patient', doc.name, 'customer', customer.name) From be2dc701c2c503aa06720376efc07c6841b0a2de Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:56:09 +0530 Subject: [PATCH 010/410] fix: vitals added naimg series, minor field rearrangements --- .../healthcare_practitioner.json | 10 +++++++--- .../sample_collection/sample_collection.json | 8 ++++---- .../doctype/vital_signs/vital_signs.json | 20 ++++++++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index fd5b6e12f66..cb747f95ef8 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_copy": 1, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -51,17 +50,20 @@ "fieldname": "first_name", "fieldtype": "Data", "label": "First Name", + "no_copy": 1, "reqd": 1 }, { "fieldname": "middle_name", "fieldtype": "Data", - "label": "Middle Name (Optional)" + "label": "Middle Name (Optional)", + "no_copy": 1 }, { "fieldname": "last_name", "fieldtype": "Data", - "label": "Last Name" + "label": "Last Name", + "no_copy": 1 }, { "fieldname": "image", @@ -226,6 +228,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Full Name", + "no_copy": 1, "read_only": 1, "search_index": 1 }, @@ -233,6 +236,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "HLC-PRAC-.YYYY.-", "report_hide": 1, "set_only_once": 1 diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 39cead88621..c352287faf4 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -9,14 +9,14 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", - "column_break_4", "patient_age", "patient_sex", + "column_break_4", + "inpatient_record", "company", + "invoiced", "section_break_6", "sample", "sample_uom", @@ -167,7 +167,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-25 16:55:52.376834", + "modified": "2020-04-04 19:17:02.707203", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 75726dbe070..fdacda6277d 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -8,11 +8,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "patient", "patient_name", - "appointment", - "encounter", "column_break_2", "signs_date", "signs_time", @@ -34,6 +31,11 @@ "bmi", "column_break_14", "nutrition_note", + "sb_references", + "inpatient_record", + "appointment", + "encounter", + "column_break_28", "company", "amended_from" ], @@ -217,7 +219,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -229,11 +230,20 @@ "options": "Vital Signs", "print_hide": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "sb_references", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-04 17:19:29.549889", + "modified": "2020-04-03 23:06:29.786184", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", From b514b26242c239533ddcebb24cbe63d218ec9a3e Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:09:06 +0530 Subject: [PATCH 011/410] fix: set company while creating encounter, fields rearranged --- .../patient_appointment.js | 3 +- .../patient_appointment.json | 43 +++++++++++++------ .../patient_appointment.py | 3 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index efa6b249b31..de8dc0e90ae 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -193,7 +193,6 @@ let check_and_set_availability = function(frm) { d.hide(); frm.enable_save(); frm.save(); - frm.enable_save(); d.get_primary_btn().attr('disabled', true); } }); @@ -400,6 +399,7 @@ let create_vital_signs = function(frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -432,6 +432,7 @@ frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { callback: function (data) { frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 7f9a671d47e..81f75975636 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -14,13 +14,16 @@ "patient_name", "patient_sex", "patient_age", - "inpatient_record", "column_break_1", - "status", - "procedure_template", - "get_procedure_from_encounter", - "procedure_prescription", + "inpatient_record", + "company", "service_unit", + "status", + "section_break_11", + "get_procedure_from_encounter", + "column_break_13", + "procedure_template", + "procedure_prescription", "section_break_12", "practitioner", "department", @@ -32,9 +35,9 @@ "duration", "section_break_16", "mode_of_payment", - "paid_amount", - "company", + "billing_item", "column_break_2", + "paid_amount", "invoiced", "ref_sales_invoice", "section_break_3", @@ -114,7 +117,8 @@ "label": "Procedure Prescription", "no_copy": 1, "options": "Procedure Prescription", - "print_hide": 1 + "print_hide": 1, + "read_only": 1 }, { "fieldname": "service_unit", @@ -140,6 +144,7 @@ "set_only_once": 1 }, { + "fetch_from": "practitioner.department", "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -234,12 +239,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "no_copy": 1, - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "collapsible": 1, @@ -282,10 +284,25 @@ "label": "Series", "options": "HLC-APP-.YYYY.-", "set_only_once": 1 + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_item", + "fieldtype": "Link", + "label": "Billing Item", + "options": "Item", + "read_only": 1 } ], "links": [], - "modified": "2020-03-27 11:27:33.773195", + "modified": "2020-04-07 11:16:34.981240", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a2d9d0240f2..3786bf28619 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -340,7 +340,8 @@ def make_encounter(source_name, target_doc=None): ['medical_department', 'department'], ['patient_sex', 'patient_sex'], ['encounter_date', 'appointment_date'], - ['invoiced', 'invoiced'] + ['invoiced', 'invoiced'], + ['company', 'company'] ] } }, target_doc) From db00dbf90e9550e31ea8036d672a7585b2e0ed87 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:11:59 +0530 Subject: [PATCH 012/410] fix: company when creatign procedure, practitioner name added --- .../patient_encounter/patient_encounter.js | 9 ++++--- .../patient_encounter/patient_encounter.json | 24 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 83c5d2be9c0..0e34164d07d 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -110,7 +110,8 @@ frappe.ui.form.on('Patient Encounter', { 'patient':data.message.patient, 'type': data.message.appointment_type, 'practitioner': data.message.practitioner, - 'invoiced': data.message.invoiced + 'invoiced': data.message.invoiced, + 'company': data.message.company }; frm.set_value(values); } @@ -209,7 +210,8 @@ let create_vital_signs = function (frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.appointment, - 'encounter': frm.doc.name + 'encounter': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -220,7 +222,8 @@ let create_procedure = function (frm) { } frappe.route_options = { 'patient': frm.doc.patient, - 'medical_department': frm.doc.medical_department + 'medical_department': frm.doc.medical_department, + 'company': frm.doc.company }; frappe.new_doc('Clinical Procedure'); }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d00e7bc7dd2..935935e0c01 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -17,9 +17,9 @@ "patient_name", "patient_sex", "patient_age", - "company", "column_break_6", "practitioner", + "practitioner_name", "medical_department", "encounter_date", "encounter_time", @@ -43,6 +43,8 @@ "sb_procedures", "procedure_prescription", "encounter_comment", + "sb_refs", + "company", "amended_from" ], "fields": [ @@ -80,7 +82,6 @@ "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", "options": "Patient", @@ -110,7 +111,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -121,7 +121,6 @@ { "fieldname": "practitioner", "fieldtype": "Link", - "in_list_view": 1, "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", @@ -272,7 +271,6 @@ "fieldname": "medical_department", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Department", "options": "Medical Department", @@ -287,11 +285,23 @@ { "fieldname": "column_break_17", "fieldtype": "Column Break" + }, + { + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-27 12:42:21.751964", + "modified": "2020-04-03 23:06:16.348846", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", @@ -318,7 +328,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "patient_name", "track_changes": 1, "track_seen": 1 } \ No newline at end of file From 35932f24912a76b1fef3b57b892df51ff92d988d Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:30:00 +0530 Subject: [PATCH 013/410] feat: multi company support --- .../healthcare/doctype/lab_test/lab_test.js | 9 ++++---- .../healthcare/doctype/lab_test/lab_test.json | 15 ++++++------- .../healthcare/doctype/lab_test/lab_test.py | 21 +++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 5b3f4c705ad..e7e44dcb481 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -137,7 +137,7 @@ var get_lab_test_prescribed = function(frm){ }); } else{ - frappe.msgprint(__("Please select Patient to get Lab Tests")); + frappe.msgprint(__("Please select a Patient to get Lab Orders")); } }; @@ -180,9 +180,10 @@ var show_lab_tests = function(frm, result){ return false; }); }); - if(!result){ - var msg = "There are no Lab Test prescribed for "+frm.doc.patient; - $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); + if(!result.length){ + var msg = "No Lab Orders found for patient " + frm.doc.patient_name + ""; + html_field.empty(); + $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } d.show(); }; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index ccbc24b3fb6..17dc1edd8c1 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -9,18 +9,18 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", "patient_name", "patient_age", "patient_sex", - "practitioner", + "report_preference", "email", "mobile", - "company", + "practitioner", "c_b", + "inpatient_record", + "company", "department", "status", "submitted_date", @@ -31,7 +31,7 @@ "employee_name", "employee_designation", "user", - "report_preference", + "invoiced", "sb_first", "lab_test_name", "column_break_26", @@ -153,7 +153,7 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", "options": "Company", "print_hide": 1, @@ -168,6 +168,7 @@ "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_standard_filter": 1, "label": "Department", "options": "Medical Department", "search_index": 1 @@ -427,7 +428,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-23 19:37:06.617764", + "modified": "2020-04-04 19:16:29.131168", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 4e4015d2f09..e49429ea625 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -69,9 +69,9 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created.")) + frappe.msgprint(_("Lab Test(s) " + lab_test_created + " created.")) else: - frappe.msgprint(_("No Lab Test created")) + frappe.msgprint(_("No Lab Tests created")) def create_lab_test_from_encounter(encounter_id): lab_test_created = False @@ -87,7 +87,7 @@ def create_lab_test_from_encounter(encounter_id): for lab_test_id in lab_test_ids: template = get_lab_test_template(lab_test_id[1]) if template: - lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template) + lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template, encounter.company) lab_test.save(ignore_permissions = True) frappe.db.set_value("Lab Prescription", lab_test_id[0], "lab_test_created", 1) if not lab_test_created: @@ -111,7 +111,7 @@ def create_lab_test_from_invoice(invoice_name): if lab_test_created != 1: template = get_lab_test_template(item.item_code) if template: - lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, company, invoice.company) if item.reference_dt == "Lab Prescription": lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) @@ -121,7 +121,7 @@ def create_lab_test_from_invoice(invoice_name): if not lab_tests_created: lab_tests_created = lab_test.name else: - lab_tests_created += ", "+lab_test.name + lab_tests_created += ", " + lab_test.name return lab_tests_created def get_lab_test_template(item): @@ -141,7 +141,7 @@ def check_template_exists(item): return template_exists return False -def create_lab_test_doc(invoiced, practitioner, patient, template): +def create_lab_test_doc(invoiced, practitioner, patient, template, company): lab_test = frappe.new_doc("Lab Test") lab_test.invoiced = invoiced lab_test.practitioner = practitioner @@ -150,11 +150,12 @@ def create_lab_test_doc(invoiced, practitioner, patient, template): lab_test.patient_sex = patient.sex lab_test.email = patient.email lab_test.mobile = patient.mobile + lab_test.report_preference = patient.report_preference lab_test.department = template.department lab_test.template = template.name lab_test.lab_test_group = template.lab_test_group lab_test.result_date = getdate() - lab_test.report_preference = patient.report_preference + lab_test.company = company return lab_test def create_normals(template, lab_test): @@ -190,7 +191,7 @@ def create_specials(template, lab_test): special.require_result_value = 1 special.template = template.name -def create_sample_doc(template, patient, invoice): +def create_sample_doc(template, patient, invoice, company = None): if template.sample: sample_exists = frappe.db.exists({ "doctype": "Sample Collection", @@ -221,6 +222,8 @@ def create_sample_doc(template, patient, invoice): sample_collection.sample = template.sample sample_collection.sample_uom = template.sample_uom sample_collection.sample_qty = template.sample_qty + sample_collection.company = company + if(template.sample_details): sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details sample_collection.save(ignore_permissions=True) @@ -229,7 +232,7 @@ def create_sample_doc(template, patient, invoice): def create_sample_collection(lab_test, template, patient, invoice): if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"): - sample_collection = create_sample_doc(template, patient, invoice) + sample_collection = create_sample_doc(template, patient, invoice, lab_test.company) if(sample_collection): lab_test.sample = sample_collection.name return lab_test From 0bdb5ba0c5110b916510477fe3e357c9f0230406 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:32:35 +0530 Subject: [PATCH 014/410] feat: multi company support --- .../clinical_procedure/clinical_procedure.js | 35 ++++++++--- .../clinical_procedure.json | 58 ++++++++++++++++--- .../clinical_procedure/clinical_procedure.py | 2 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 5f36bdd95c6..213fa3de1f5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -175,16 +175,37 @@ frappe.ui.form.on('Clinical Procedure', { name: frm.doc.appointment }, callback: function(data) { - frm.set_value('patient', data.message.patient); - frm.set_value('procedure_template', data.message.procedure_template); - frm.set_value('medical_department', data.message.department); - frm.set_value('start_date', data.message.appointment_date); - frm.set_value('start_time', data.message.appointment_time); - frm.set_value('notes', data.message.notes); - frm.set_value('service_unit', data.message.service_unit); + let values = { + 'patient':data.message.patient, + 'procedure_template': data.message.procedure_template, + 'medical_department': data.message.department, + 'start_date': data.message.appointment_date, + 'start_time': data.message.appointment_time, + 'notes': data.message.notes, + 'service_unit': data.message.service_unit, + 'company': data.message.company + } + frm.set_value(values); } }); } + else{ + let values = { + 'patient': '', + 'patient_name': '', + 'patient_sex': '', + 'patient_age': '', + 'medical_department': '', + 'procedure_template': '', + 'start_date': '', + 'start_time': '', + 'notes': '', + 'service_unit': '', + 'inpatient_record': '' + // 'inpatient_status': '' + }; + frm.set_value(values); + } }, procedure_template: function(frm) { diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 3c936bbf277..59a87eccde4 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -7,16 +7,18 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "procedure_template", "appointment", + "column_break_30", + "procedure_template", + "section_break_6", "patient", + "patient_name", "patient_sex", "patient_age", - "prescription", "medical_department", "practitioner", + "practitioner_name", "column_break_7", "status", "service_unit", @@ -26,7 +28,6 @@ "sample", "invoiced", "notes", - "company", "consumables_section", "consume_stock", "items", @@ -36,6 +37,11 @@ "consumable_total_amount", "column_break_27", "consumption_details", + "sb_refs", + "inpatient_record", + "company", + "column_break_34", + "prescription", "amended_from" ], "fields": [ @@ -56,7 +62,7 @@ { "fieldname": "appointment", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Appointment", "options": "Patient Appointment" }, @@ -64,7 +70,7 @@ "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Patient", "options": "Patient", "reqd": 1 @@ -88,17 +94,20 @@ "fieldtype": "Link", "hidden": 1, "label": "Procedure Prescription", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "read_only": 1 }, { "fieldname": "medical_department", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Medical Department", "options": "Medical Department" }, { "fieldname": "practitioner", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner" }, @@ -237,11 +246,43 @@ { "fieldname": "section_break_24", "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-02 11:44:27.970651", + "modified": "2020-04-03 23:06:04.009856", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -263,5 +304,6 @@ "restrict_to_domain": "Healthcare", "sort_field": "modified", "sort_order": "DESC", + "title_field": "patient_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index db3afc8807e..56617e52586 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -37,7 +37,7 @@ class ClinicalProcedure(Document): template = frappe.get_doc('Clinical Procedure Template', self.procedure_template) if template.sample: patient = frappe.get_doc('Patient', self.patient) - sample_collection = create_sample_doc(template, patient, None) + sample_collection = create_sample_doc(template, patient, None, self.company) frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name) self.reload() From 93d6794d286e211c60c38681ce6edf21c6438538 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:34:28 +0530 Subject: [PATCH 015/410] company field added --- .../inpatient_record/inpatient_record.json | 1165 ++++------------- 1 file changed, 220 insertions(+), 945 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index 92c11fbb4da..c1b516d5363 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -1,980 +1,255 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-07-11 17:48:51.404139", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2018-07-11 17:48:51.404139", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "naming_series", + "patient", + "patient_name", + "gender", + "blood_group", + "dob", + "mobile", + "email", + "phone", + "column_break_8", + "company", + "status", + "scheduled_date", + "admitted_datetime", + "expected_discharge", + "discharge_date", + "references", + "cb_admission", + "admission_practitioner", + "admission_encounter", + "cb_discharge", + "discharge_practitioner", + "discharge_encounter", + "sb_inpatient_occupancy", + "inpatient_occupancies", + "btn_transfer", + "sb_discharge_note", + "discharge_note" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, { - "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": 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": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-INP-.YYYY.-", - "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": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "HLC-INP-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "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": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "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 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.sex", - "fieldname": "gender", - "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "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 - }, + "fetch_from": "patient.sex", + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.blood_group", - "fieldname": "blood_group", - "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": "Blood Group", - "length": 0, - "no_copy": 0, - "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", - "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 - }, + "fetch_from": "patient.blood_group", + "fieldname": "blood_group", + "fieldtype": "Select", + "label": "Blood Group", + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dob", - "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 of birth", - "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 - }, + "fetch_from": "patient.dob", + "fieldname": "dob", + "fieldtype": "Date", + "label": "Date of birth", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.mobile", - "fieldname": "mobile", - "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": "Mobile", - "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 - }, + "fetch_from": "patient.mobile", + "fieldname": "mobile", + "fieldtype": "Data", + "label": "Mobile", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.email", - "fieldname": "email", - "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": "Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "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 - }, + "fetch_from": "patient.email", + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.phone", - "fieldname": "phone", - "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": "Phone", - "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 - }, + "fetch_from": "patient.phone", + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "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_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "scheduled_date", - "fieldtype": "Date", - "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": "Admission Schedule 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 - }, + "default": "Today", + "fieldname": "scheduled_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Admission Schedule Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "admitted_datetime", - "fieldtype": "Datetime", - "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": "Admitted Datetime", - "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 - }, + "default": "Today", + "fieldname": "admitted_datetime", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Admitted Datetime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expected_discharge", - "fieldtype": "Date", - "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": "Expected Discharge", - "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": "expected_discharge", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expected Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_date", - "fieldtype": "Date", - "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": "Discharge 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "references", - "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": "References", - "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": "references", + "fieldtype": "Section Break", + "label": "References" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_admission", - "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, - "label": "Admission", - "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": "cb_admission", + "fieldtype": "Column Break", + "label": "Admission" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_discharge", - "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, - "label": "Discharge", - "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": "cb_discharge", + "fieldtype": "Column Break", + "label": "Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_inpatient_occupancy", - "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": "Inpatient Occupancy", - "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": "sb_inpatient_occupancy", + "fieldtype": "Section Break", + "label": "Inpatient Occupancy" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inpatient_occupancies", - "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, - "length": 0, - "no_copy": 0, - "options": "Inpatient Occupancy", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_occupancies", + "fieldtype": "Table", + "options": "Inpatient Occupancy", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "btn_transfer", - "fieldtype": "Button", - "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": "Transfer", - "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": "btn_transfer", + "fieldtype": "Button", + "label": "Transfer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.status != \"Admission Scheduled\"", - "fieldname": "sb_discharge_note", - "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": "Discharge Note", - "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 - }, + "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "fieldname": "sb_discharge_note", + "fieldtype": "Section Break", + "label": "Discharge Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_note", - "fieldtype": "Text Editor", - "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": "", - "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": "discharge_note", + "fieldtype": "Text Editor" + }, + { + "fetch_from": "admission_encounter.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:43.168245", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Inpatient Record", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-04-07 13:13:39.351977", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Inpatient Record", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1 +} \ No newline at end of file From 2bdf21bd3062425a6d34fe6a505afac73edcf8b7 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:42:03 +0530 Subject: [PATCH 016/410] feat: multi-company billing sales onvoice - filter get items based on company utils - company filters in all get item helper methods utils - refactor appointemnt items --- .../doctype/sales_invoice/sales_invoice.js | 7 +- erpnext/healthcare/utils.py | 96 ++++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 6f78db2ccc3..7741842fca1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -919,7 +919,7 @@ var get_healthcare_services_to_invoice = function(frm) { if(patient && patient!=selected_patient){ selected_patient = patient; var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; - var args = {patient: patient}; + var args = {patient: patient, company: frm.doc.company}; var columns = (["service", "reference_name", "reference_type"]); get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); } @@ -1063,7 +1063,10 @@ var get_drugs_to_invoice = function(frm) { description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', get_query: function(doc) { return { - filters: { patient: dialog.get_value("patient"), docstatus: 1 } + filters: { patient: dialog.get_value("patient"), + company: frm.doc.company, + docstatus: 1 + } }; } }, diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 246242ad84c..e9cc597e813 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -3,82 +3,84 @@ # For license information, please see license.txt from __future__ import unicode_literals +import math +import json import frappe from frappe import _ -import math from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() -def get_healthcare_services_to_invoice(patient): +def get_healthcare_services_to_invoice(patient, company): patient = frappe.get_doc('Patient', patient) + items_to_invoice = [] if patient: validate_customer_created(patient) - items_to_invoice = [] - patient_appointments = frappe.get_list( - 'Patient Appointment', - fields='*', - filters={'patient': patient.name, 'invoiced': 0}, - order_by='appointment_date' - ) - if patient_appointments: - items_to_invoice = get_fee_validity(patient_appointments) + # Customer validated, build a list of billable services + items_to_invoice += get_appointments_to_invoice(patient, company) + items_to_invoice += get_encounters_to_invoice(patient, company) + items_to_invoice += get_lab_tests_to_invoice(patient, company) + items_to_invoice += get_clinical_procedures_to_invoice(patient, company) + items_to_invoice += get_inpatient_services_to_invoice(patient, company) - encounters = get_encounters_to_invoice(patient) - lab_tests = get_lab_tests_to_invoice(patient) - clinical_procedures = get_clinical_procedures_to_invoice(patient) - inpatient_services = get_inpatient_services_to_invoice(patient) - - items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services return items_to_invoice + def validate_customer_created(patient): if not frappe.db.get_value('Patient', patient.name, 'customer'): msg = _("Please set a Customer linked to the Patient") msg += " {0}".format(patient.name) frappe.throw(msg, title=_('Customer Not Found')) -def get_fee_validity(patient_appointments): - if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): - return - items_to_invoice = [] +def get_appointments_to_invoice(patient, company): + appointments_to_invoice = [] + patient_appointments = frappe.get_list( + 'Patient Appointment', + fields = '*', + filters = {'patient': patient.name, 'company': company, 'invoiced': 0}, + order_by = 'appointment_date' + ) + for appointment in patient_appointments: + # Procedure Appointments if appointment.procedure_template: if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): - items_to_invoice.append({ + appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', 'reference_name': appointment.name, 'service': appointment.procedure_template }) + # Consultation Appointments, should check fee validity else: - fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}) - if not fee_validity: - practitioner_charge = 0 - income_account = None - service_item = None - if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) - income_account = get_income_account(appointment.practitioner, appointment.company) - items_to_invoice.append({ - 'reference_type': 'Patient Appointment', - 'reference_name': appointment.name, - 'service': service_item, - 'rate': practitioner_charge, - 'income_account': income_account - }) + if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \ + frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}): + continue # Skip invoicing, fee validty present + practitioner_charge = 0 + income_account = None + service_item = None + if appointment.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + income_account = get_income_account(appointment.practitioner, appointment.company) + appointments_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) - return items_to_invoice + return appointments_to_invoice -def get_encounters_to_invoice(patient): +def get_encounters_to_invoice(patient, company): encounters_to_invoice = [] encounters = frappe.get_list( 'Patient Encounter', fields=['*'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) if encounters: for encounter in encounters: @@ -101,12 +103,12 @@ def get_encounters_to_invoice(patient): return encounters_to_invoice -def get_lab_tests_to_invoice(patient): +def get_lab_tests_to_invoice(patient, company): lab_tests_to_invoice = [] lab_tests = frappe.get_list( 'Lab Test', fields=['name', 'template'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) for lab_test in lab_tests: item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable']) @@ -142,12 +144,12 @@ def get_lab_tests_to_invoice(patient): return lab_tests_to_invoice -def get_clinical_procedures_to_invoice(patient): +def get_clinical_procedures_to_invoice(patient, company): clinical_procedures_to_invoice = [] procedures = frappe.get_list( 'Clinical Procedure', fields='*', - filters={'patient': patient.name, 'invoiced': False} + filters={'patient': patient.name, 'company': company, 'invoiced': False} ) for procedure in procedures: if not procedure.appointment: @@ -203,7 +205,7 @@ def get_clinical_procedures_to_invoice(patient): return clinical_procedures_to_invoice -def get_inpatient_services_to_invoice(patient): +def get_inpatient_services_to_invoice(patient, company): services_to_invoice = [] inpatient_services = frappe.db.sql( ''' @@ -213,10 +215,11 @@ def get_inpatient_services_to_invoice(patient): `tabInpatient Record` ip, `tabInpatient Occupancy` io WHERE ip.patient=%s + and ip.company=%s and io.parent=ip.name and io.left=1 and io.invoiced=0 - ''', (patient.name), as_dict=1) + ''', (patient.name, company), as_dict=1) for inpatient_occupancy in inpatient_services: service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type') @@ -376,6 +379,7 @@ def check_fee_validity(appointment): def manage_fee_validity(appointment): fee_validity = check_fee_validity(appointment) + if fee_validity: if appointment.status == 'Cancelled' and fee_validity.visited > 0: fee_validity.visited -= 1 From c1e25eabfd067ed927c68622906944be82e57839 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:36:06 +0530 Subject: [PATCH 017/410] feat: additional customer fileds add customer_group, price list, currency etc. rearrange and group fields disable cusotmer edit after save (temp fix) --- erpnext/healthcare/doctype/patient/patient.js | 1 + .../healthcare/doctype/patient/patient.json | 81 +++++++++++++------ erpnext/healthcare/doctype/patient/patient.py | 10 ++- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index d5df9567ec5..a11faf58066 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -40,6 +40,7 @@ frappe.ui.form.on('Patient', { frm.add_custom_button(__('Patient Encounter'), function () { create_encounter(frm); }, 'Create'); + frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option } }, onload: function (frm) { diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 4258e4011de..2c701fbf944 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -24,13 +24,20 @@ "image", "column_break_14", "status", - "inpatient_status", "inpatient_record", - "customer", + "inpatient_status", + "report_preference", "mobile", "email", "phone", - "report_preference", + "customer_details_section", + "customer", + "customer_group", + "territory", + "column_break_24", + "default_currency", + "default_price_list", + "language", "personal_and_social_history", "occupation", "column_break_25", @@ -52,9 +59,7 @@ "surrounding_factors", "other_risk_factors", "more_info", - "patient_details", - "ac_sb", - "default_currency" + "patient_details" ], "fields": [ { @@ -156,6 +161,7 @@ "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_preview": 1, "label": "Customer", "options": "Customer" }, @@ -171,7 +177,8 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Mobile" + "label": "Mobile", + "options": "Phone" }, { "bold": 1, @@ -186,7 +193,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "label": "Phone" + "label": "Phone", + "options": "Phone" }, { "collapsible": 1, @@ -268,25 +276,25 @@ "fieldname": "tobacco_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Past)" + "label": "Tobacco Consumption (Past)" }, { "fieldname": "tobacco_current_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Present)" + "label": "Tobacco Consumption (Present)" }, { "fieldname": "alcohol_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Alcohol Consumption Habbits (Past)" + "label": "Alcohol Consumption (Past)" }, { "fieldname": "alcohol_current_use", "fieldtype": "Data", "ignore_user_permissions": 1, - "label": "Alcohol Consumption Habbits (Present)" + "label": "Alcohol Consumption (Present)" }, { "fieldname": "column_break_32", @@ -320,20 +328,11 @@ "ignore_xss_filter": 1, "label": "Patient Details" }, - { - "collapsible": 1, - "fieldname": "ac_sb", - "fieldtype": "Section Break", - "label": "Account Details" - }, { "fieldname": "default_currency", "fieldtype": "Link", - "hidden": 1, - "ignore_xss_filter": 1, - "label": "Default Currency", - "options": "Currency", - "print_hide": 1 + "label": "Billing Currency", + "options": "Currency" }, { "fieldname": "last_name", @@ -351,13 +350,47 @@ "fieldname": "middle_name", "fieldtype": "Data", "label": "Middle Name (optional)" + }, + { + "collapsible": 1, + "fieldname": "customer_details_section", + "fieldtype": "Section Break", + "label": "Customer Details" + }, + { + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_price_list", + "fieldtype": "Link", + "label": "Default Price List", + "options": "Price List" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "image_field": "image", "links": [], "max_attachments": 50, - "modified": "2020-04-06 12:55:30.807744", + "modified": "2020-04-11 14:53:48.767245", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e304a0bbc36..f83fac03a63 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -26,6 +26,7 @@ class Patient(Document): frappe.db.set_value('Patient', self.name, 'status', 'Disabled') else: send_registration_sms(self) + self.reload() # self.notify_update() def set_full_name(self): if self.last_name: @@ -86,8 +87,8 @@ class Patient(Document): return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') - territory = frappe.db.get_single_value('Selling Settings', 'territory') + customer_group = doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group') + territory = doc.territory or frappe.db.get_single_value('Selling Settings', 'territory') if not (customer_group and territory): customer_group = get_root_of('Customer Group') territory = get_root_of('Territory') @@ -98,7 +99,10 @@ def create_customer(doc): 'customer_name': doc.patient_name, 'customer_group': customer_group, 'territory' : territory, - 'customer_type': 'Individual' + 'customer_type': 'Individual', + 'default_currency': doc.default_currency, + 'default_price_ist': doc.default_price_list, + 'language': doc.language }).insert(ignore_permissions=True, ignore_mandatory=True) frappe.db.set_value('Patient', doc.name, 'customer', customer.name) From e2a92246ea6ef72cfe266ba53136bc88c79838d2 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:56:09 +0530 Subject: [PATCH 018/410] fix: vitals added naimg series, minor field rearrangements --- .../healthcare_practitioner.json | 10 +++++++--- .../sample_collection/sample_collection.json | 8 ++++---- .../doctype/vital_signs/vital_signs.json | 20 ++++++++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index fd5b6e12f66..cb747f95ef8 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_copy": 1, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -51,17 +50,20 @@ "fieldname": "first_name", "fieldtype": "Data", "label": "First Name", + "no_copy": 1, "reqd": 1 }, { "fieldname": "middle_name", "fieldtype": "Data", - "label": "Middle Name (Optional)" + "label": "Middle Name (Optional)", + "no_copy": 1 }, { "fieldname": "last_name", "fieldtype": "Data", - "label": "Last Name" + "label": "Last Name", + "no_copy": 1 }, { "fieldname": "image", @@ -226,6 +228,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Full Name", + "no_copy": 1, "read_only": 1, "search_index": 1 }, @@ -233,6 +236,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "HLC-PRAC-.YYYY.-", "report_hide": 1, "set_only_once": 1 diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 39cead88621..c352287faf4 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -9,14 +9,14 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", - "column_break_4", "patient_age", "patient_sex", + "column_break_4", + "inpatient_record", "company", + "invoiced", "section_break_6", "sample", "sample_uom", @@ -167,7 +167,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-25 16:55:52.376834", + "modified": "2020-04-04 19:17:02.707203", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 75726dbe070..fdacda6277d 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -8,11 +8,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "patient", "patient_name", - "appointment", - "encounter", "column_break_2", "signs_date", "signs_time", @@ -34,6 +31,11 @@ "bmi", "column_break_14", "nutrition_note", + "sb_references", + "inpatient_record", + "appointment", + "encounter", + "column_break_28", "company", "amended_from" ], @@ -217,7 +219,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -229,11 +230,20 @@ "options": "Vital Signs", "print_hide": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "sb_references", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-04 17:19:29.549889", + "modified": "2020-04-03 23:06:29.786184", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", From b72d18c469dd9289e3ce0b69e6c6a8aa7fe151ae Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:09:06 +0530 Subject: [PATCH 019/410] fix: set company while creating encounter, fields rearranged --- .../patient_appointment.js | 3 +- .../patient_appointment.json | 43 +++++++++++++------ .../patient_appointment.py | 3 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index efa6b249b31..de8dc0e90ae 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -193,7 +193,6 @@ let check_and_set_availability = function(frm) { d.hide(); frm.enable_save(); frm.save(); - frm.enable_save(); d.get_primary_btn().attr('disabled', true); } }); @@ -400,6 +399,7 @@ let create_vital_signs = function(frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -432,6 +432,7 @@ frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { callback: function (data) { frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 7f9a671d47e..81f75975636 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -14,13 +14,16 @@ "patient_name", "patient_sex", "patient_age", - "inpatient_record", "column_break_1", - "status", - "procedure_template", - "get_procedure_from_encounter", - "procedure_prescription", + "inpatient_record", + "company", "service_unit", + "status", + "section_break_11", + "get_procedure_from_encounter", + "column_break_13", + "procedure_template", + "procedure_prescription", "section_break_12", "practitioner", "department", @@ -32,9 +35,9 @@ "duration", "section_break_16", "mode_of_payment", - "paid_amount", - "company", + "billing_item", "column_break_2", + "paid_amount", "invoiced", "ref_sales_invoice", "section_break_3", @@ -114,7 +117,8 @@ "label": "Procedure Prescription", "no_copy": 1, "options": "Procedure Prescription", - "print_hide": 1 + "print_hide": 1, + "read_only": 1 }, { "fieldname": "service_unit", @@ -140,6 +144,7 @@ "set_only_once": 1 }, { + "fetch_from": "practitioner.department", "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -234,12 +239,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "no_copy": 1, - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "collapsible": 1, @@ -282,10 +284,25 @@ "label": "Series", "options": "HLC-APP-.YYYY.-", "set_only_once": 1 + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_item", + "fieldtype": "Link", + "label": "Billing Item", + "options": "Item", + "read_only": 1 } ], "links": [], - "modified": "2020-03-27 11:27:33.773195", + "modified": "2020-04-07 11:16:34.981240", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a2d9d0240f2..3786bf28619 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -340,7 +340,8 @@ def make_encounter(source_name, target_doc=None): ['medical_department', 'department'], ['patient_sex', 'patient_sex'], ['encounter_date', 'appointment_date'], - ['invoiced', 'invoiced'] + ['invoiced', 'invoiced'], + ['company', 'company'] ] } }, target_doc) From b66833edfe2dd16499d37b50bce2d02f295e6d5a Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:11:59 +0530 Subject: [PATCH 020/410] fix: company when creatign procedure, practitioner name added --- .../patient_encounter/patient_encounter.js | 9 ++++--- .../patient_encounter/patient_encounter.json | 24 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 83c5d2be9c0..0e34164d07d 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -110,7 +110,8 @@ frappe.ui.form.on('Patient Encounter', { 'patient':data.message.patient, 'type': data.message.appointment_type, 'practitioner': data.message.practitioner, - 'invoiced': data.message.invoiced + 'invoiced': data.message.invoiced, + 'company': data.message.company }; frm.set_value(values); } @@ -209,7 +210,8 @@ let create_vital_signs = function (frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.appointment, - 'encounter': frm.doc.name + 'encounter': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -220,7 +222,8 @@ let create_procedure = function (frm) { } frappe.route_options = { 'patient': frm.doc.patient, - 'medical_department': frm.doc.medical_department + 'medical_department': frm.doc.medical_department, + 'company': frm.doc.company }; frappe.new_doc('Clinical Procedure'); }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d00e7bc7dd2..935935e0c01 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -17,9 +17,9 @@ "patient_name", "patient_sex", "patient_age", - "company", "column_break_6", "practitioner", + "practitioner_name", "medical_department", "encounter_date", "encounter_time", @@ -43,6 +43,8 @@ "sb_procedures", "procedure_prescription", "encounter_comment", + "sb_refs", + "company", "amended_from" ], "fields": [ @@ -80,7 +82,6 @@ "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", "options": "Patient", @@ -110,7 +111,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -121,7 +121,6 @@ { "fieldname": "practitioner", "fieldtype": "Link", - "in_list_view": 1, "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", @@ -272,7 +271,6 @@ "fieldname": "medical_department", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Department", "options": "Medical Department", @@ -287,11 +285,23 @@ { "fieldname": "column_break_17", "fieldtype": "Column Break" + }, + { + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-27 12:42:21.751964", + "modified": "2020-04-03 23:06:16.348846", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", @@ -318,7 +328,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "patient_name", "track_changes": 1, "track_seen": 1 } \ No newline at end of file From aa1b9b57ac1a6bc3841685cdc3f01e24e1948784 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:30:00 +0530 Subject: [PATCH 021/410] feat: multi company support --- .../healthcare/doctype/lab_test/lab_test.js | 9 ++++---- .../healthcare/doctype/lab_test/lab_test.json | 15 ++++++------- .../healthcare/doctype/lab_test/lab_test.py | 21 +++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 5b3f4c705ad..e7e44dcb481 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -137,7 +137,7 @@ var get_lab_test_prescribed = function(frm){ }); } else{ - frappe.msgprint(__("Please select Patient to get Lab Tests")); + frappe.msgprint(__("Please select a Patient to get Lab Orders")); } }; @@ -180,9 +180,10 @@ var show_lab_tests = function(frm, result){ return false; }); }); - if(!result){ - var msg = "There are no Lab Test prescribed for "+frm.doc.patient; - $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); + if(!result.length){ + var msg = "No Lab Orders found for patient " + frm.doc.patient_name + ""; + html_field.empty(); + $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } d.show(); }; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index ccbc24b3fb6..17dc1edd8c1 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -9,18 +9,18 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", "patient_name", "patient_age", "patient_sex", - "practitioner", + "report_preference", "email", "mobile", - "company", + "practitioner", "c_b", + "inpatient_record", + "company", "department", "status", "submitted_date", @@ -31,7 +31,7 @@ "employee_name", "employee_designation", "user", - "report_preference", + "invoiced", "sb_first", "lab_test_name", "column_break_26", @@ -153,7 +153,7 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", "options": "Company", "print_hide": 1, @@ -168,6 +168,7 @@ "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_standard_filter": 1, "label": "Department", "options": "Medical Department", "search_index": 1 @@ -427,7 +428,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-23 19:37:06.617764", + "modified": "2020-04-04 19:16:29.131168", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 4e4015d2f09..e49429ea625 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -69,9 +69,9 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created.")) + frappe.msgprint(_("Lab Test(s) " + lab_test_created + " created.")) else: - frappe.msgprint(_("No Lab Test created")) + frappe.msgprint(_("No Lab Tests created")) def create_lab_test_from_encounter(encounter_id): lab_test_created = False @@ -87,7 +87,7 @@ def create_lab_test_from_encounter(encounter_id): for lab_test_id in lab_test_ids: template = get_lab_test_template(lab_test_id[1]) if template: - lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template) + lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template, encounter.company) lab_test.save(ignore_permissions = True) frappe.db.set_value("Lab Prescription", lab_test_id[0], "lab_test_created", 1) if not lab_test_created: @@ -111,7 +111,7 @@ def create_lab_test_from_invoice(invoice_name): if lab_test_created != 1: template = get_lab_test_template(item.item_code) if template: - lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, company, invoice.company) if item.reference_dt == "Lab Prescription": lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) @@ -121,7 +121,7 @@ def create_lab_test_from_invoice(invoice_name): if not lab_tests_created: lab_tests_created = lab_test.name else: - lab_tests_created += ", "+lab_test.name + lab_tests_created += ", " + lab_test.name return lab_tests_created def get_lab_test_template(item): @@ -141,7 +141,7 @@ def check_template_exists(item): return template_exists return False -def create_lab_test_doc(invoiced, practitioner, patient, template): +def create_lab_test_doc(invoiced, practitioner, patient, template, company): lab_test = frappe.new_doc("Lab Test") lab_test.invoiced = invoiced lab_test.practitioner = practitioner @@ -150,11 +150,12 @@ def create_lab_test_doc(invoiced, practitioner, patient, template): lab_test.patient_sex = patient.sex lab_test.email = patient.email lab_test.mobile = patient.mobile + lab_test.report_preference = patient.report_preference lab_test.department = template.department lab_test.template = template.name lab_test.lab_test_group = template.lab_test_group lab_test.result_date = getdate() - lab_test.report_preference = patient.report_preference + lab_test.company = company return lab_test def create_normals(template, lab_test): @@ -190,7 +191,7 @@ def create_specials(template, lab_test): special.require_result_value = 1 special.template = template.name -def create_sample_doc(template, patient, invoice): +def create_sample_doc(template, patient, invoice, company = None): if template.sample: sample_exists = frappe.db.exists({ "doctype": "Sample Collection", @@ -221,6 +222,8 @@ def create_sample_doc(template, patient, invoice): sample_collection.sample = template.sample sample_collection.sample_uom = template.sample_uom sample_collection.sample_qty = template.sample_qty + sample_collection.company = company + if(template.sample_details): sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details sample_collection.save(ignore_permissions=True) @@ -229,7 +232,7 @@ def create_sample_doc(template, patient, invoice): def create_sample_collection(lab_test, template, patient, invoice): if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"): - sample_collection = create_sample_doc(template, patient, invoice) + sample_collection = create_sample_doc(template, patient, invoice, lab_test.company) if(sample_collection): lab_test.sample = sample_collection.name return lab_test From d856a9aed3931274795006563a1daec11ebabd02 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:32:35 +0530 Subject: [PATCH 022/410] feat: multi company support --- .../clinical_procedure/clinical_procedure.js | 35 ++++++++--- .../clinical_procedure.json | 58 ++++++++++++++++--- .../clinical_procedure/clinical_procedure.py | 2 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 5f36bdd95c6..213fa3de1f5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -175,16 +175,37 @@ frappe.ui.form.on('Clinical Procedure', { name: frm.doc.appointment }, callback: function(data) { - frm.set_value('patient', data.message.patient); - frm.set_value('procedure_template', data.message.procedure_template); - frm.set_value('medical_department', data.message.department); - frm.set_value('start_date', data.message.appointment_date); - frm.set_value('start_time', data.message.appointment_time); - frm.set_value('notes', data.message.notes); - frm.set_value('service_unit', data.message.service_unit); + let values = { + 'patient':data.message.patient, + 'procedure_template': data.message.procedure_template, + 'medical_department': data.message.department, + 'start_date': data.message.appointment_date, + 'start_time': data.message.appointment_time, + 'notes': data.message.notes, + 'service_unit': data.message.service_unit, + 'company': data.message.company + } + frm.set_value(values); } }); } + else{ + let values = { + 'patient': '', + 'patient_name': '', + 'patient_sex': '', + 'patient_age': '', + 'medical_department': '', + 'procedure_template': '', + 'start_date': '', + 'start_time': '', + 'notes': '', + 'service_unit': '', + 'inpatient_record': '' + // 'inpatient_status': '' + }; + frm.set_value(values); + } }, procedure_template: function(frm) { diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 3c936bbf277..59a87eccde4 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -7,16 +7,18 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "procedure_template", "appointment", + "column_break_30", + "procedure_template", + "section_break_6", "patient", + "patient_name", "patient_sex", "patient_age", - "prescription", "medical_department", "practitioner", + "practitioner_name", "column_break_7", "status", "service_unit", @@ -26,7 +28,6 @@ "sample", "invoiced", "notes", - "company", "consumables_section", "consume_stock", "items", @@ -36,6 +37,11 @@ "consumable_total_amount", "column_break_27", "consumption_details", + "sb_refs", + "inpatient_record", + "company", + "column_break_34", + "prescription", "amended_from" ], "fields": [ @@ -56,7 +62,7 @@ { "fieldname": "appointment", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Appointment", "options": "Patient Appointment" }, @@ -64,7 +70,7 @@ "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Patient", "options": "Patient", "reqd": 1 @@ -88,17 +94,20 @@ "fieldtype": "Link", "hidden": 1, "label": "Procedure Prescription", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "read_only": 1 }, { "fieldname": "medical_department", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Medical Department", "options": "Medical Department" }, { "fieldname": "practitioner", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner" }, @@ -237,11 +246,43 @@ { "fieldname": "section_break_24", "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-02 11:44:27.970651", + "modified": "2020-04-03 23:06:04.009856", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -263,5 +304,6 @@ "restrict_to_domain": "Healthcare", "sort_field": "modified", "sort_order": "DESC", + "title_field": "patient_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index db3afc8807e..56617e52586 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -37,7 +37,7 @@ class ClinicalProcedure(Document): template = frappe.get_doc('Clinical Procedure Template', self.procedure_template) if template.sample: patient = frappe.get_doc('Patient', self.patient) - sample_collection = create_sample_doc(template, patient, None) + sample_collection = create_sample_doc(template, patient, None, self.company) frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name) self.reload() From 05e34ee9fb1b1bbf4a9c49b83de63eb1ecd8776f Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:34:28 +0530 Subject: [PATCH 023/410] company field added --- .../inpatient_record/inpatient_record.json | 1165 ++++------------- 1 file changed, 220 insertions(+), 945 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index 92c11fbb4da..c1b516d5363 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -1,980 +1,255 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-07-11 17:48:51.404139", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2018-07-11 17:48:51.404139", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "naming_series", + "patient", + "patient_name", + "gender", + "blood_group", + "dob", + "mobile", + "email", + "phone", + "column_break_8", + "company", + "status", + "scheduled_date", + "admitted_datetime", + "expected_discharge", + "discharge_date", + "references", + "cb_admission", + "admission_practitioner", + "admission_encounter", + "cb_discharge", + "discharge_practitioner", + "discharge_encounter", + "sb_inpatient_occupancy", + "inpatient_occupancies", + "btn_transfer", + "sb_discharge_note", + "discharge_note" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, { - "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": 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": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-INP-.YYYY.-", - "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": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "HLC-INP-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "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": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "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 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.sex", - "fieldname": "gender", - "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "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 - }, + "fetch_from": "patient.sex", + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.blood_group", - "fieldname": "blood_group", - "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": "Blood Group", - "length": 0, - "no_copy": 0, - "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", - "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 - }, + "fetch_from": "patient.blood_group", + "fieldname": "blood_group", + "fieldtype": "Select", + "label": "Blood Group", + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dob", - "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 of birth", - "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 - }, + "fetch_from": "patient.dob", + "fieldname": "dob", + "fieldtype": "Date", + "label": "Date of birth", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.mobile", - "fieldname": "mobile", - "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": "Mobile", - "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 - }, + "fetch_from": "patient.mobile", + "fieldname": "mobile", + "fieldtype": "Data", + "label": "Mobile", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.email", - "fieldname": "email", - "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": "Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "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 - }, + "fetch_from": "patient.email", + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.phone", - "fieldname": "phone", - "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": "Phone", - "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 - }, + "fetch_from": "patient.phone", + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "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_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "scheduled_date", - "fieldtype": "Date", - "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": "Admission Schedule 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 - }, + "default": "Today", + "fieldname": "scheduled_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Admission Schedule Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "admitted_datetime", - "fieldtype": "Datetime", - "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": "Admitted Datetime", - "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 - }, + "default": "Today", + "fieldname": "admitted_datetime", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Admitted Datetime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expected_discharge", - "fieldtype": "Date", - "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": "Expected Discharge", - "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": "expected_discharge", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expected Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_date", - "fieldtype": "Date", - "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": "Discharge 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "references", - "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": "References", - "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": "references", + "fieldtype": "Section Break", + "label": "References" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_admission", - "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, - "label": "Admission", - "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": "cb_admission", + "fieldtype": "Column Break", + "label": "Admission" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_discharge", - "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, - "label": "Discharge", - "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": "cb_discharge", + "fieldtype": "Column Break", + "label": "Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_inpatient_occupancy", - "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": "Inpatient Occupancy", - "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": "sb_inpatient_occupancy", + "fieldtype": "Section Break", + "label": "Inpatient Occupancy" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inpatient_occupancies", - "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, - "length": 0, - "no_copy": 0, - "options": "Inpatient Occupancy", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_occupancies", + "fieldtype": "Table", + "options": "Inpatient Occupancy", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "btn_transfer", - "fieldtype": "Button", - "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": "Transfer", - "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": "btn_transfer", + "fieldtype": "Button", + "label": "Transfer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.status != \"Admission Scheduled\"", - "fieldname": "sb_discharge_note", - "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": "Discharge Note", - "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 - }, + "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "fieldname": "sb_discharge_note", + "fieldtype": "Section Break", + "label": "Discharge Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_note", - "fieldtype": "Text Editor", - "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": "", - "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": "discharge_note", + "fieldtype": "Text Editor" + }, + { + "fetch_from": "admission_encounter.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:43.168245", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Inpatient Record", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-04-07 13:13:39.351977", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Inpatient Record", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1 +} \ No newline at end of file From 4f3c46840f54a6c3399ab6f09a934503517c05d4 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:42:03 +0530 Subject: [PATCH 024/410] feat: multi-company billing sales onvoice - filter get items based on company utils - company filters in all get item helper methods utils - refactor appointemnt items --- .../doctype/sales_invoice/sales_invoice.js | 7 +- erpnext/healthcare/utils.py | 96 ++++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 6f78db2ccc3..7741842fca1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -919,7 +919,7 @@ var get_healthcare_services_to_invoice = function(frm) { if(patient && patient!=selected_patient){ selected_patient = patient; var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; - var args = {patient: patient}; + var args = {patient: patient, company: frm.doc.company}; var columns = (["service", "reference_name", "reference_type"]); get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); } @@ -1063,7 +1063,10 @@ var get_drugs_to_invoice = function(frm) { description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', get_query: function(doc) { return { - filters: { patient: dialog.get_value("patient"), docstatus: 1 } + filters: { patient: dialog.get_value("patient"), + company: frm.doc.company, + docstatus: 1 + } }; } }, diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 246242ad84c..e9cc597e813 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -3,82 +3,84 @@ # For license information, please see license.txt from __future__ import unicode_literals +import math +import json import frappe from frappe import _ -import math from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() -def get_healthcare_services_to_invoice(patient): +def get_healthcare_services_to_invoice(patient, company): patient = frappe.get_doc('Patient', patient) + items_to_invoice = [] if patient: validate_customer_created(patient) - items_to_invoice = [] - patient_appointments = frappe.get_list( - 'Patient Appointment', - fields='*', - filters={'patient': patient.name, 'invoiced': 0}, - order_by='appointment_date' - ) - if patient_appointments: - items_to_invoice = get_fee_validity(patient_appointments) + # Customer validated, build a list of billable services + items_to_invoice += get_appointments_to_invoice(patient, company) + items_to_invoice += get_encounters_to_invoice(patient, company) + items_to_invoice += get_lab_tests_to_invoice(patient, company) + items_to_invoice += get_clinical_procedures_to_invoice(patient, company) + items_to_invoice += get_inpatient_services_to_invoice(patient, company) - encounters = get_encounters_to_invoice(patient) - lab_tests = get_lab_tests_to_invoice(patient) - clinical_procedures = get_clinical_procedures_to_invoice(patient) - inpatient_services = get_inpatient_services_to_invoice(patient) - - items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services return items_to_invoice + def validate_customer_created(patient): if not frappe.db.get_value('Patient', patient.name, 'customer'): msg = _("Please set a Customer linked to the Patient") msg += " {0}".format(patient.name) frappe.throw(msg, title=_('Customer Not Found')) -def get_fee_validity(patient_appointments): - if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): - return - items_to_invoice = [] +def get_appointments_to_invoice(patient, company): + appointments_to_invoice = [] + patient_appointments = frappe.get_list( + 'Patient Appointment', + fields = '*', + filters = {'patient': patient.name, 'company': company, 'invoiced': 0}, + order_by = 'appointment_date' + ) + for appointment in patient_appointments: + # Procedure Appointments if appointment.procedure_template: if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): - items_to_invoice.append({ + appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', 'reference_name': appointment.name, 'service': appointment.procedure_template }) + # Consultation Appointments, should check fee validity else: - fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}) - if not fee_validity: - practitioner_charge = 0 - income_account = None - service_item = None - if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) - income_account = get_income_account(appointment.practitioner, appointment.company) - items_to_invoice.append({ - 'reference_type': 'Patient Appointment', - 'reference_name': appointment.name, - 'service': service_item, - 'rate': practitioner_charge, - 'income_account': income_account - }) + if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \ + frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}): + continue # Skip invoicing, fee validty present + practitioner_charge = 0 + income_account = None + service_item = None + if appointment.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + income_account = get_income_account(appointment.practitioner, appointment.company) + appointments_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) - return items_to_invoice + return appointments_to_invoice -def get_encounters_to_invoice(patient): +def get_encounters_to_invoice(patient, company): encounters_to_invoice = [] encounters = frappe.get_list( 'Patient Encounter', fields=['*'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) if encounters: for encounter in encounters: @@ -101,12 +103,12 @@ def get_encounters_to_invoice(patient): return encounters_to_invoice -def get_lab_tests_to_invoice(patient): +def get_lab_tests_to_invoice(patient, company): lab_tests_to_invoice = [] lab_tests = frappe.get_list( 'Lab Test', fields=['name', 'template'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) for lab_test in lab_tests: item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable']) @@ -142,12 +144,12 @@ def get_lab_tests_to_invoice(patient): return lab_tests_to_invoice -def get_clinical_procedures_to_invoice(patient): +def get_clinical_procedures_to_invoice(patient, company): clinical_procedures_to_invoice = [] procedures = frappe.get_list( 'Clinical Procedure', fields='*', - filters={'patient': patient.name, 'invoiced': False} + filters={'patient': patient.name, 'company': company, 'invoiced': False} ) for procedure in procedures: if not procedure.appointment: @@ -203,7 +205,7 @@ def get_clinical_procedures_to_invoice(patient): return clinical_procedures_to_invoice -def get_inpatient_services_to_invoice(patient): +def get_inpatient_services_to_invoice(patient, company): services_to_invoice = [] inpatient_services = frappe.db.sql( ''' @@ -213,10 +215,11 @@ def get_inpatient_services_to_invoice(patient): `tabInpatient Record` ip, `tabInpatient Occupancy` io WHERE ip.patient=%s + and ip.company=%s and io.parent=ip.name and io.left=1 and io.invoiced=0 - ''', (patient.name), as_dict=1) + ''', (patient.name, company), as_dict=1) for inpatient_occupancy in inpatient_services: service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type') @@ -376,6 +379,7 @@ def check_fee_validity(appointment): def manage_fee_validity(appointment): fee_validity = check_fee_validity(appointment) + if fee_validity: if appointment.status == 'Cancelled' and fee_validity.visited > 0: fee_validity.visited -= 1 From 8179804405f6955ba35487a08516ce9486f9be3c Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 14 Apr 2020 23:05:11 +0530 Subject: [PATCH 025/410] refactor: Quoted Item Comparison Report - Refactored Report View - Added Chart - Added 'valid_till' date in Supplier Quotation - Added patch to set valid_till and daily job to set expiry status --- .../supplier_quotation/supplier_quotation.js | 4 + .../supplier_quotation.json | 13 +- .../supplier_quotation/supplier_quotation.py | 16 +- .../quoted_item_comparison.js | 91 ++++++++-- .../quoted_item_comparison.py | 161 ++++++++++-------- erpnext/hooks.py | 3 +- erpnext/patches.txt | 1 + ...t_valid_till_date_in_supplier_quotation.py | 8 + 8 files changed, 207 insertions(+), 90 deletions(-) create mode 100644 erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 16061c61ba0..1b8b40459f6 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -18,6 +18,10 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext refresh: function() { var me = this; this._super(); + + if (this.frm.doc.__islocal && !this.frm.doc.valid_till) { + this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1)); + } if (this.frm.doc.docstatus === 1) { cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __('Create')); diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 82fc6285bcf..6964e783ee8 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -13,9 +13,10 @@ "supplier", "supplier_name", "column_break1", - "transaction_date", - "amended_from", "company", + "transaction_date", + "valid_till", + "amended_from", "address_section", "supplier_address", "contact_person", @@ -791,13 +792,19 @@ "options": "Opportunity", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "valid_till", + "fieldtype": "Date", + "label": "Valid Till", + "reqd": 1 } ], "icon": "fa fa-shopping-cart", "idx": 29, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:17:28.208693", + "modified": "2020-04-14 22:43:32.248415", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index 5b4356a7471..baf245735a4 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, nowdate, add_days +from frappe.utils import flt, nowdate, add_days, getdate from frappe.model.mapper import get_mapped_doc from erpnext.controllers.buying_controller import BuyingController @@ -28,6 +28,7 @@ class SupplierQuotation(BuyingController): validate_for_items(self) self.validate_with_previous_doc() self.validate_uom_is_integer("uom", "qty") + self.validate_valid_till() def on_submit(self): frappe.db.set(self, "status", "Submitted") @@ -52,6 +53,11 @@ class SupplierQuotation(BuyingController): "is_child_table": True } }) + + def validate_valid_till(self): + if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date): + frappe.throw(_("Valid till Date cannot be before Transaction Date")) + def update_rfq_supplier_status(self, include_me): rfq_list = set([]) for item in self.items: @@ -158,3 +164,11 @@ def make_quotation(source_name, target_doc=None): }, target_doc) return doclist + +def set_expired_status(): + frappe.db.sql(""" + UPDATE + `tabSupplier Quotation` SET `status` = 'Expired' + WHERE + `status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s + """, (nowdate())) \ No newline at end of file diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js index 3d05612c9ed..f331beb49e3 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js @@ -5,13 +5,11 @@ frappe.query_reports["Quoted Item Comparison"] = { filters: [ { fieldtype: "Link", - label: __("Supplier Quotation"), - options: "Supplier Quotation", - fieldname: "supplier_quotation", - default: "", - get_query: () => { - return { filters: { "docstatus": ["<", 2] } } - } + label: __("Company"), + options: "Company", + fieldname: "company", + default: frappe.defaults.get_user_default("Company"), + "reqd": 1 }, { reqd: 1, @@ -37,8 +35,83 @@ frappe.query_reports["Quoted Item Comparison"] = { } } } + }, + { + fieldtype: "Link", + label: __("Supplier Quotation"), + options: "Supplier Quotation", + fieldname: "supplier_quotation", + default: "", + get_query: () => { + return { filters: { "docstatus": ["<", 2] } } + } + }, + { + fieldtype: "Link", + label: __("Request for Quotation"), + options: "Request for Quotation", + fieldname: "request_for_quotation", + default: "", + get_query: () => { + return { filters: { "docstatus": ["<", 2] } } + } } ], + + prepare_chart_data: (result) => { + let supplier_wise_map = {}, data_points_map = {}; + let qty_list = result.map(res=> res.qty); + qty_list = new Set(qty_list); + + // create supplier wise map like in Report + for(let res of result){ + if(!(res.supplier in supplier_wise_map)){ + supplier_wise_map[res.supplier]= {}; + } + supplier_wise_map[res.supplier][res.qty] = res.price; + } + + // create datapoints for each qty + for(let supplier of Object.keys(supplier_wise_map)) { + let row = supplier_wise_map[supplier]; + for(let qty of qty_list){ + if(!data_points_map[qty]){ + data_points_map[qty] = [] + } + if(row[qty]){ + data_points_map[qty].push(row[qty]); + } + else{ + data_points_map[qty].push(null); + } + } + } + + let dataset = []; + qty_list.forEach((qty) => { + let datapoints = { + 'name': 'Price for Qty ' + qty, + 'values': data_points_map[qty] + } + dataset.push(datapoints); + + }); + return dataset; + }, + + get_chart_data: function (columns, result) { + let suppliers = result.filter(d => d.supplier_name).map(res => res.supplier_name); + let dataset = frappe.query_reports["Quoted Item Comparison"].prepare_chart_data(result); + + return { + data: { + labels: suppliers, + datasets: dataset + }, + type: 'bar' + } + }, + onload: (report) => { // Create a button for setting the default supplier report.page.add_inner_button(__("Select Default Supplier"), () => { @@ -102,6 +175,4 @@ frappe.query_reports["Quoted Item Comparison"] = { }); dialog.show(); } -} - - +} \ No newline at end of file diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py index 5aff6bacaea..bb1067a05a0 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py @@ -2,103 +2,114 @@ # For license information, please see license.txt from __future__ import unicode_literals -from erpnext.setup.utils import get_exchange_rate -from frappe.utils import flt, cint import frappe +from frappe.utils import flt, cint +from collections import defaultdict +from erpnext.setup.utils import get_exchange_rate def execute(filters=None): - qty_list = get_quantity_list(filters.item) - data = get_quote_list(filters.item, qty_list) - columns = get_columns(qty_list) + conditions = get_conditions(filters) + data = get_data(filters, conditions) + columns = get_columns() return columns, data - -def get_quote_list(item, qty_list): - out = [] + +def get_data(filters, conditions): + out, suppliers = [], [] + item = filters.get("item") + if not item: return [] - suppliers = [] - price_data = [] company_currency = frappe.db.get_default("currency") - float_precision = cint(frappe.db.get_default("float_precision")) or 2 - # Get the list of suppliers - for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item` - where item_code=%s and docstatus < 2""", item, as_dict=1): - for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation` - where name =%s and docstatus < 2""", root.parent, as_dict=1): - ip = frappe._dict({ - "supplier": splr.supplier, - "qty": root.qty, - "parent": root.parent, - "rate": root.rate - }) - price_data.append(ip) - suppliers.append(splr.supplier) + float_precision = cint(frappe.db.get_default("float_precision")) or 2 - #Add a row for each supplier - for root in set(suppliers): - supplier_currency = frappe.db.get_value("Supplier", root, "default_currency") + supplier_quotation_data = frappe.db.sql("""SELECT + sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation, + sq.supplier + FROM + `tabSupplier Quotation Item` sqi, + `tabSupplier Quotation` sq + WHERE + sqi.item_code = '{0}' + AND sqi.parent = sq.name + AND sqi.docstatus < 2 + AND sq.company = '{1}' + AND sq.status != 'Expired' + {2}""".format(item, filters.get("company"), conditions), as_dict=1) + + supplier_wise_map = defaultdict(list) + + for data in supplier_quotation_data: + supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency") if supplier_currency: exchange_rate = get_exchange_rate(supplier_currency, company_currency) else: exchange_rate = 1 - row = frappe._dict({ - "supplier_name": root - }) - for col in qty_list: - # Get the quantity for this row - for item_price in price_data: - if str(item_price.qty) == col.key and item_price.supplier == root: - row[col.key] = flt(item_price.rate * exchange_rate, float_precision) - row[col.key + "QUOTE"] = item_price.parent - break - else: - row[col.key] = "" - row[col.key + "QUOTE"] = "" - out.append(row) - - return out - -def get_quantity_list(item): - out = [] - - if item: - qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item` - where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1) + row = { + "quotation": data.get("parent"), + "qty": data.get("qty"), + "price": flt(data.get("rate") * exchange_rate, float_precision), + "request_for_quotation": data.get("request_for_quotation"), + "supplier": data.get("supplier") # used for chart generation + } - for qt in qty_list: - col = frappe._dict({ - "key": str(qt.qty), - "label": "Qty: " + str(int(qt.qty)) - }) - out.append(col) + supplier_wise_map[data.supplier].append(row) + suppliers.append(data.supplier) + + suppliers = set(suppliers) + + for supplier in suppliers: + supplier_wise_map[supplier][0].update({"supplier_name": supplier}) + for entry in supplier_wise_map[supplier]: + out.append(entry) return out - -def get_columns(qty_list): + +def get_conditions(filters): + conditions = "" + + if filters.get("request_for_quotation"): + conditions += " AND sqi.request_for_quotation = '{0}' ".format(filters.get("request_for_quotation")) + + return conditions + + +def get_columns(): columns = [{ "fieldname": "supplier_name", "label": "Supplier", "fieldtype": "Link", "options": "Supplier", "width": 200 - }] - - for qty in qty_list: - columns.append({ - "fieldname": qty.key, - "label": qty.label, - "fieldtype": "Currency", - "options": "currency", - "width": 80 - }) - columns.append({ - "fieldname": qty.key + "QUOTE", - "label": "Quotation", - "fieldtype": "Link", - "options": "Supplier Quotation", - "width": 90 - }) + }, + { + "fieldname": "quotation", + "label": "Supplier Quotation", + "fieldtype": "Link", + "options": "Supplier Quotation", + "width": 200 + }, + { + "fieldname": "qty", + "label": "Quantity", + "fieldtype": "Float", + "width": 80 + }, + { + "fieldname": "price", + "label": "Price", + "fieldtype": "Currency", + "options": "Company:company:default_currency", + "width": 110 + }, + { + "fieldname": "request_for_quotation", + "label": "Request for Quotation", + "fieldtype": "Link", + "options": "Request for Quotation", + "width": 200 + } + ] return columns \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 6199cb22643..a1f47b8f9c0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -304,7 +304,8 @@ scheduler_events = { "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status", "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts", "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status", - "erpnext.selling.doctype.quotation.quotation.set_expired_status" + "erpnext.selling.doctype.quotation.quotation.set_expired_status", + "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status" ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9b5e5d02fbd..a2a393b96a4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -666,3 +666,4 @@ erpnext.patches.v12_0.recalculate_requested_qty_in_bin erpnext.patches.v12_0.set_total_batch_quantity erpnext.patches.v12_0.rename_mws_settings_fields erpnext.patches.v12_0.set_updated_purpose_in_pick_list +erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py new file mode 100644 index 00000000000..0f24ec6756b --- /dev/null +++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + reload_doc("buying", "doctype", "suppplier_quotation") + frappe.db.sql("""UPDATE `tabSupplier Quotation` + SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH) + WHERE docstatus < 2""") \ No newline at end of file From 8890b6044d27381a0e9b83614fce6a7db53093f3 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 15 Apr 2020 12:18:10 +0530 Subject: [PATCH 026/410] fix: Patch fix, Travis fix and cleanup - Added UOM column in Report - Removed mandatory on `valid_till` - Added list view indicator for Expired status in Supplier Quotation - Sorted Labels in Chart and syntax cleanup - Made labels Translatable - Fixed patch --- .../supplier_quotation.json | 5 ++-- .../supplier_quotation_list.js | 2 ++ .../quoted_item_comparison.js | 24 +++++++++---------- .../quoted_item_comparison.py | 19 +++++++++++---- ...t_valid_till_date_in_supplier_quotation.py | 2 +- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 6964e783ee8..3bc441af6d1 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -796,15 +796,14 @@ { "fieldname": "valid_till", "fieldtype": "Date", - "label": "Valid Till", - "reqd": 1 + "label": "Valid Till" } ], "icon": "fa fa-shopping-cart", "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-04-14 22:43:32.248415", + "modified": "2020-04-15 11:44:52.958022", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js index 95554397bb7..9f4fecea86b 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Supplier Quotation'] = { return [__("Ordered"), "green", "status,=,Ordered"]; } else if(doc.status==="Rejected") { return [__("Lost"), "darkgrey", "status,=,Lost"]; + } else if(doc.status==="Expired") { + return [__("Expired"), "darkgrey", "status,=,Expired"]; } } }; diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js index f331beb49e3..fe4abd8c9c5 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js @@ -60,28 +60,29 @@ frappe.query_reports["Quoted Item Comparison"] = { prepare_chart_data: (result) => { let supplier_wise_map = {}, data_points_map = {}; - let qty_list = result.map(res=> res.qty); + let qty_list = result.map(res => res.qty); + qty_list.sort(); qty_list = new Set(qty_list); // create supplier wise map like in Report - for(let res of result){ - if(!(res.supplier in supplier_wise_map)){ - supplier_wise_map[res.supplier]= {}; + for (let res of result) { + if (!(res.supplier in supplier_wise_map)) { + supplier_wise_map[res.supplier] = {}; } supplier_wise_map[res.supplier][res.qty] = res.price; } // create datapoints for each qty - for(let supplier of Object.keys(supplier_wise_map)) { + for (let supplier of Object.keys(supplier_wise_map)) { let row = supplier_wise_map[supplier]; - for(let qty of qty_list){ - if(!data_points_map[qty]){ - data_points_map[qty] = [] + for (let qty of qty_list) { + if (!data_points_map[qty]) { + data_points_map[qty] = []; } - if(row[qty]){ + if (row[qty]) { data_points_map[qty].push(row[qty]); } - else{ + else { data_points_map[qty].push(null); } } @@ -90,11 +91,10 @@ frappe.query_reports["Quoted Item Comparison"] = { let dataset = []; qty_list.forEach((qty) => { let datapoints = { - 'name': 'Price for Qty ' + qty, + 'name': __('Price for Qty ') + qty, 'values': data_points_map[qty] } dataset.push(datapoints); - }); return dataset; }, diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py index bb1067a05a0..fd7a7311983 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt, cint +from frappe import _ from collections import defaultdict from erpnext.setup.utils import get_exchange_rate @@ -50,6 +51,7 @@ def get_data(filters, conditions): "quotation": data.get("parent"), "qty": data.get("qty"), "price": flt(data.get("rate") * exchange_rate, float_precision), + "uom": data.get("uom"), "request_for_quotation": data.get("request_for_quotation"), "supplier": data.get("supplier") # used for chart generation } @@ -78,34 +80,41 @@ def get_conditions(filters): def get_columns(): columns = [{ "fieldname": "supplier_name", - "label": "Supplier", + "label": _("Supplier"), "fieldtype": "Link", "options": "Supplier", "width": 200 }, { "fieldname": "quotation", - "label": "Supplier Quotation", + "label": _("Supplier Quotation"), "fieldtype": "Link", "options": "Supplier Quotation", "width": 200 }, { "fieldname": "qty", - "label": "Quantity", + "label": _("Quantity"), "fieldtype": "Float", "width": 80 }, { "fieldname": "price", - "label": "Price", + "label": _("Price"), "fieldtype": "Currency", "options": "Company:company:default_currency", "width": 110 }, + { + "fieldname": "uom", + "label": _("UOM"), + "fieldtype": "Link", + "options": "UOM", + "width": 90 + }, { "fieldname": "request_for_quotation", - "label": "Request for Quotation", + "label": _("Request for Quotation"), "fieldtype": "Link", "options": "Request for Quotation", "width": 200 diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py index 0f24ec6756b..befa46c31d5 100644 --- a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py +++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe def execute(): - reload_doc("buying", "doctype", "suppplier_quotation") + frappe.reload_doc("buying", "doctype", "suppplier_quotation") frappe.db.sql("""UPDATE `tabSupplier Quotation` SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH) WHERE docstatus < 2""") \ No newline at end of file From 79b01838c37f6fd6d1efa098b5fa353c2a842048 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 16 Apr 2020 12:52:03 +0530 Subject: [PATCH 027/410] fix: set patient while billing, tests fixed --- .../patient_appointment.py | 2 +- .../test_patient_appointment.py | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 3786bf28619..cc3492a810f 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -119,6 +119,7 @@ def invoice_appointment(appointment_doc): if automate_invoicing and not appointment_invoiced and not fee_validity: sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.patient = appointment_doc.patient sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() @@ -339,7 +340,6 @@ def make_encounter(source_name, target_doc=None): ['practitioner', 'practitioner'], ['medical_department', 'department'], ['patient_sex', 'patient_sex'], - ['encounter_date', 'appointment_date'], ['invoiced', 'invoiced'], ['company', 'company'] ] diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 7075af5d00c..7cdb28e0da1 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter from frappe.utils import nowdate, add_days from frappe.utils.make_random import get_random @@ -23,6 +23,14 @@ class TestPatientAppointment(unittest.TestCase): create_encounter(appointment) self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') + def test_start_encounter(self): + patient, medical_department, practitioner = create_healthcare_docs() + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 3)) + encounter = create_encounter(appointment) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'company'), appointment.company) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'practitioner'), appointment.practitioner) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'patient'), appointment.patient) + def test_invoicing(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) @@ -33,7 +41,11 @@ class TestPatientAppointment(unittest.TestCase): frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1) self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) - self.assertTrue(frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice')) + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'company'), appointment.company) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) def test_appointment_cancel(self): patient, medical_department, practitioner = create_healthcare_docs() @@ -53,8 +65,8 @@ class TestPatientAppointment(unittest.TestCase): appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) update_status(appointment.name, 'Cancelled') # check invoice cancelled - sales_invoice = frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice') - self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice, 'status'), 'Cancelled') + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled') def create_healthcare_docs(): @@ -90,14 +102,9 @@ def create_patient(): patient = patient.name return patient -def create_encounter(appointment=None): - encounter = frappe.new_doc('Patient Encounter') +def create_encounter(appointment): if appointment: - encounter.appointment = appointment.name - encounter.patient = appointment.patient - encounter.practitioner = appointment.practitioner - encounter.encounter_date = appointment.appointment_date - encounter.encounter_time = appointment.appointment_time + encounter = make_encounter(appointment.name) encounter.save() encounter.submit() return encounter From d4f5a6928f37b6838de6bdcbea9094b595d9fb24 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:36:06 +0530 Subject: [PATCH 028/410] feat: additional customer fileds add customer_group, price list, currency etc. rearrange and group fields disable cusotmer edit after save (temp fix) --- erpnext/healthcare/doctype/patient/patient.js | 1 + .../healthcare/doctype/patient/patient.json | 81 +++++++++++++------ erpnext/healthcare/doctype/patient/patient.py | 10 ++- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index d5df9567ec5..a11faf58066 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -40,6 +40,7 @@ frappe.ui.form.on('Patient', { frm.add_custom_button(__('Patient Encounter'), function () { create_encounter(frm); }, 'Create'); + frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option } }, onload: function (frm) { diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 4258e4011de..2c701fbf944 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -24,13 +24,20 @@ "image", "column_break_14", "status", - "inpatient_status", "inpatient_record", - "customer", + "inpatient_status", + "report_preference", "mobile", "email", "phone", - "report_preference", + "customer_details_section", + "customer", + "customer_group", + "territory", + "column_break_24", + "default_currency", + "default_price_list", + "language", "personal_and_social_history", "occupation", "column_break_25", @@ -52,9 +59,7 @@ "surrounding_factors", "other_risk_factors", "more_info", - "patient_details", - "ac_sb", - "default_currency" + "patient_details" ], "fields": [ { @@ -156,6 +161,7 @@ "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_preview": 1, "label": "Customer", "options": "Customer" }, @@ -171,7 +177,8 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Mobile" + "label": "Mobile", + "options": "Phone" }, { "bold": 1, @@ -186,7 +193,8 @@ "fieldname": "phone", "fieldtype": "Data", "in_filter": 1, - "label": "Phone" + "label": "Phone", + "options": "Phone" }, { "collapsible": 1, @@ -268,25 +276,25 @@ "fieldname": "tobacco_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Past)" + "label": "Tobacco Consumption (Past)" }, { "fieldname": "tobacco_current_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Tobacco Consumption Habbits (Present)" + "label": "Tobacco Consumption (Present)" }, { "fieldname": "alcohol_past_use", "fieldtype": "Data", "ignore_xss_filter": 1, - "label": "Alcohol Consumption Habbits (Past)" + "label": "Alcohol Consumption (Past)" }, { "fieldname": "alcohol_current_use", "fieldtype": "Data", "ignore_user_permissions": 1, - "label": "Alcohol Consumption Habbits (Present)" + "label": "Alcohol Consumption (Present)" }, { "fieldname": "column_break_32", @@ -320,20 +328,11 @@ "ignore_xss_filter": 1, "label": "Patient Details" }, - { - "collapsible": 1, - "fieldname": "ac_sb", - "fieldtype": "Section Break", - "label": "Account Details" - }, { "fieldname": "default_currency", "fieldtype": "Link", - "hidden": 1, - "ignore_xss_filter": 1, - "label": "Default Currency", - "options": "Currency", - "print_hide": 1 + "label": "Billing Currency", + "options": "Currency" }, { "fieldname": "last_name", @@ -351,13 +350,47 @@ "fieldname": "middle_name", "fieldtype": "Data", "label": "Middle Name (optional)" + }, + { + "collapsible": 1, + "fieldname": "customer_details_section", + "fieldtype": "Section Break", + "label": "Customer Details" + }, + { + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_price_list", + "fieldtype": "Link", + "label": "Default Price List", + "options": "Price List" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" } ], "icon": "fa fa-user", "image_field": "image", "links": [], "max_attachments": 50, - "modified": "2020-04-06 12:55:30.807744", + "modified": "2020-04-11 14:53:48.767245", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index e304a0bbc36..f83fac03a63 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -26,6 +26,7 @@ class Patient(Document): frappe.db.set_value('Patient', self.name, 'status', 'Disabled') else: send_registration_sms(self) + self.reload() # self.notify_update() def set_full_name(self): if self.last_name: @@ -86,8 +87,8 @@ class Patient(Document): return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') - territory = frappe.db.get_single_value('Selling Settings', 'territory') + customer_group = doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group') + territory = doc.territory or frappe.db.get_single_value('Selling Settings', 'territory') if not (customer_group and territory): customer_group = get_root_of('Customer Group') territory = get_root_of('Territory') @@ -98,7 +99,10 @@ def create_customer(doc): 'customer_name': doc.patient_name, 'customer_group': customer_group, 'territory' : territory, - 'customer_type': 'Individual' + 'customer_type': 'Individual', + 'default_currency': doc.default_currency, + 'default_price_ist': doc.default_price_list, + 'language': doc.language }).insert(ignore_permissions=True, ignore_mandatory=True) frappe.db.set_value('Patient', doc.name, 'customer', customer.name) From 82aeff0a3256c978be733896c00ad577df861c88 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 11:56:09 +0530 Subject: [PATCH 029/410] fix: vitals added naimg series, minor field rearrangements --- .../healthcare_practitioner.json | 10 +++++++--- .../sample_collection/sample_collection.json | 8 ++++---- .../doctype/vital_signs/vital_signs.json | 20 ++++++++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json index fd5b6e12f66..cb747f95ef8 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_copy": 1, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -51,17 +50,20 @@ "fieldname": "first_name", "fieldtype": "Data", "label": "First Name", + "no_copy": 1, "reqd": 1 }, { "fieldname": "middle_name", "fieldtype": "Data", - "label": "Middle Name (Optional)" + "label": "Middle Name (Optional)", + "no_copy": 1 }, { "fieldname": "last_name", "fieldtype": "Data", - "label": "Last Name" + "label": "Last Name", + "no_copy": 1 }, { "fieldname": "image", @@ -226,6 +228,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Full Name", + "no_copy": 1, "read_only": 1, "search_index": 1 }, @@ -233,6 +236,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "HLC-PRAC-.YYYY.-", "report_hide": 1, "set_only_once": 1 diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 39cead88621..c352287faf4 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -9,14 +9,14 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", - "column_break_4", "patient_age", "patient_sex", + "column_break_4", + "inpatient_record", "company", + "invoiced", "section_break_6", "sample", "sample_uom", @@ -167,7 +167,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-25 16:55:52.376834", + "modified": "2020-04-04 19:17:02.707203", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 75726dbe070..fdacda6277d 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -8,11 +8,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "patient", "patient_name", - "appointment", - "encounter", "column_break_2", "signs_date", "signs_time", @@ -34,6 +31,11 @@ "bmi", "column_break_14", "nutrition_note", + "sb_references", + "inpatient_record", + "appointment", + "encounter", + "column_break_28", "company", "amended_from" ], @@ -217,7 +219,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -229,11 +230,20 @@ "options": "Vital Signs", "print_hide": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "sb_references", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-04 17:19:29.549889", + "modified": "2020-04-03 23:06:29.786184", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", From 9c7d45deae18e299a9fd1baf2b08a8086e02c19d Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:09:06 +0530 Subject: [PATCH 030/410] fix: set company while creating encounter, fields rearranged --- .../patient_appointment.js | 3 +- .../patient_appointment.json | 43 +++++++++++++------ .../patient_appointment.py | 3 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index efa6b249b31..de8dc0e90ae 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -193,7 +193,6 @@ let check_and_set_availability = function(frm) { d.hide(); frm.enable_save(); frm.save(); - frm.enable_save(); d.get_primary_btn().attr('disabled', true); } }); @@ -400,6 +399,7 @@ let create_vital_signs = function(frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -432,6 +432,7 @@ frappe.ui.form.on('Patient Appointment', 'practitioner', function(frm) { callback: function (data) { frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department); frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge); + frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item); } }); } diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 7f9a671d47e..81f75975636 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -14,13 +14,16 @@ "patient_name", "patient_sex", "patient_age", - "inpatient_record", "column_break_1", - "status", - "procedure_template", - "get_procedure_from_encounter", - "procedure_prescription", + "inpatient_record", + "company", "service_unit", + "status", + "section_break_11", + "get_procedure_from_encounter", + "column_break_13", + "procedure_template", + "procedure_prescription", "section_break_12", "practitioner", "department", @@ -32,9 +35,9 @@ "duration", "section_break_16", "mode_of_payment", - "paid_amount", - "company", + "billing_item", "column_break_2", + "paid_amount", "invoiced", "ref_sales_invoice", "section_break_3", @@ -114,7 +117,8 @@ "label": "Procedure Prescription", "no_copy": 1, "options": "Procedure Prescription", - "print_hide": 1 + "print_hide": 1, + "read_only": 1 }, { "fieldname": "service_unit", @@ -140,6 +144,7 @@ "set_only_once": 1 }, { + "fetch_from": "practitioner.department", "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -234,12 +239,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "no_copy": 1, - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "collapsible": 1, @@ -282,10 +284,25 @@ "label": "Series", "options": "HLC-APP-.YYYY.-", "set_only_once": 1 + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_item", + "fieldtype": "Link", + "label": "Billing Item", + "options": "Item", + "read_only": 1 } ], "links": [], - "modified": "2020-03-27 11:27:33.773195", + "modified": "2020-04-07 11:16:34.981240", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a2d9d0240f2..3786bf28619 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -340,7 +340,8 @@ def make_encounter(source_name, target_doc=None): ['medical_department', 'department'], ['patient_sex', 'patient_sex'], ['encounter_date', 'appointment_date'], - ['invoiced', 'invoiced'] + ['invoiced', 'invoiced'], + ['company', 'company'] ] } }, target_doc) From 80ab98bf3676bb65acd3081142ecf33a5026c93d Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 12:11:59 +0530 Subject: [PATCH 031/410] fix: company when creatign procedure, practitioner name added --- .../patient_encounter/patient_encounter.js | 9 ++++--- .../patient_encounter/patient_encounter.json | 24 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 83c5d2be9c0..0e34164d07d 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -110,7 +110,8 @@ frappe.ui.form.on('Patient Encounter', { 'patient':data.message.patient, 'type': data.message.appointment_type, 'practitioner': data.message.practitioner, - 'invoiced': data.message.invoiced + 'invoiced': data.message.invoiced, + 'company': data.message.company }; frm.set_value(values); } @@ -209,7 +210,8 @@ let create_vital_signs = function (frm) { frappe.route_options = { 'patient': frm.doc.patient, 'appointment': frm.doc.appointment, - 'encounter': frm.doc.name + 'encounter': frm.doc.name, + 'company': frm.doc.company }; frappe.new_doc('Vital Signs'); }; @@ -220,7 +222,8 @@ let create_procedure = function (frm) { } frappe.route_options = { 'patient': frm.doc.patient, - 'medical_department': frm.doc.medical_department + 'medical_department': frm.doc.medical_department, + 'company': frm.doc.company }; frappe.new_doc('Clinical Procedure'); }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index d00e7bc7dd2..935935e0c01 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -17,9 +17,9 @@ "patient_name", "patient_sex", "patient_age", - "company", "column_break_6", "practitioner", + "practitioner_name", "medical_department", "encounter_date", "encounter_time", @@ -43,6 +43,8 @@ "sb_procedures", "procedure_prescription", "encounter_comment", + "sb_refs", + "company", "amended_from" ], "fields": [ @@ -80,7 +82,6 @@ "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Patient", "options": "Patient", @@ -110,7 +111,6 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, "label": "Company", "options": "Company" }, @@ -121,7 +121,6 @@ { "fieldname": "practitioner", "fieldtype": "Link", - "in_list_view": 1, "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner", @@ -272,7 +271,6 @@ "fieldname": "medical_department", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_list_view": 1, "in_standard_filter": 1, "label": "Department", "options": "Medical Department", @@ -287,11 +285,23 @@ { "fieldname": "column_break_17", "fieldtype": "Column Break" + }, + { + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-27 12:42:21.751964", + "modified": "2020-04-03 23:06:16.348846", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", @@ -318,7 +328,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "patient_name", "track_changes": 1, "track_seen": 1 } \ No newline at end of file From 2d2db60035e7afe7c83098085d66e999e53d7e4b Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:30:00 +0530 Subject: [PATCH 032/410] feat: multi company support --- .../healthcare/doctype/lab_test/lab_test.js | 9 ++++---- .../healthcare/doctype/lab_test/lab_test.json | 15 ++++++------- .../healthcare/doctype/lab_test/lab_test.py | 21 +++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 5b3f4c705ad..e7e44dcb481 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -137,7 +137,7 @@ var get_lab_test_prescribed = function(frm){ }); } else{ - frappe.msgprint(__("Please select Patient to get Lab Tests")); + frappe.msgprint(__("Please select a Patient to get Lab Orders")); } }; @@ -180,9 +180,10 @@ var show_lab_tests = function(frm, result){ return false; }); }); - if(!result){ - var msg = "There are no Lab Test prescribed for "+frm.doc.patient; - $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); + if(!result.length){ + var msg = "No Lab Orders found for patient " + frm.doc.patient_name + ""; + html_field.empty(); + $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } d.show(); }; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index ccbc24b3fb6..17dc1edd8c1 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -9,18 +9,18 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "invoiced", "patient", "patient_name", "patient_age", "patient_sex", - "practitioner", + "report_preference", "email", "mobile", - "company", + "practitioner", "c_b", + "inpatient_record", + "company", "department", "status", "submitted_date", @@ -31,7 +31,7 @@ "employee_name", "employee_designation", "user", - "report_preference", + "invoiced", "sb_first", "lab_test_name", "column_break_26", @@ -153,7 +153,7 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", "options": "Company", "print_hide": 1, @@ -168,6 +168,7 @@ "fieldname": "department", "fieldtype": "Link", "ignore_user_permissions": 1, + "in_standard_filter": 1, "label": "Department", "options": "Medical Department", "search_index": 1 @@ -427,7 +428,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-23 19:37:06.617764", + "modified": "2020-04-04 19:16:29.131168", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 4e4015d2f09..e49429ea625 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -69,9 +69,9 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created.")) + frappe.msgprint(_("Lab Test(s) " + lab_test_created + " created.")) else: - frappe.msgprint(_("No Lab Test created")) + frappe.msgprint(_("No Lab Tests created")) def create_lab_test_from_encounter(encounter_id): lab_test_created = False @@ -87,7 +87,7 @@ def create_lab_test_from_encounter(encounter_id): for lab_test_id in lab_test_ids: template = get_lab_test_template(lab_test_id[1]) if template: - lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template) + lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template, encounter.company) lab_test.save(ignore_permissions = True) frappe.db.set_value("Lab Prescription", lab_test_id[0], "lab_test_created", 1) if not lab_test_created: @@ -111,7 +111,7 @@ def create_lab_test_from_invoice(invoice_name): if lab_test_created != 1: template = get_lab_test_template(item.item_code) if template: - lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template) + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, company, invoice.company) if item.reference_dt == "Lab Prescription": lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) @@ -121,7 +121,7 @@ def create_lab_test_from_invoice(invoice_name): if not lab_tests_created: lab_tests_created = lab_test.name else: - lab_tests_created += ", "+lab_test.name + lab_tests_created += ", " + lab_test.name return lab_tests_created def get_lab_test_template(item): @@ -141,7 +141,7 @@ def check_template_exists(item): return template_exists return False -def create_lab_test_doc(invoiced, practitioner, patient, template): +def create_lab_test_doc(invoiced, practitioner, patient, template, company): lab_test = frappe.new_doc("Lab Test") lab_test.invoiced = invoiced lab_test.practitioner = practitioner @@ -150,11 +150,12 @@ def create_lab_test_doc(invoiced, practitioner, patient, template): lab_test.patient_sex = patient.sex lab_test.email = patient.email lab_test.mobile = patient.mobile + lab_test.report_preference = patient.report_preference lab_test.department = template.department lab_test.template = template.name lab_test.lab_test_group = template.lab_test_group lab_test.result_date = getdate() - lab_test.report_preference = patient.report_preference + lab_test.company = company return lab_test def create_normals(template, lab_test): @@ -190,7 +191,7 @@ def create_specials(template, lab_test): special.require_result_value = 1 special.template = template.name -def create_sample_doc(template, patient, invoice): +def create_sample_doc(template, patient, invoice, company = None): if template.sample: sample_exists = frappe.db.exists({ "doctype": "Sample Collection", @@ -221,6 +222,8 @@ def create_sample_doc(template, patient, invoice): sample_collection.sample = template.sample sample_collection.sample_uom = template.sample_uom sample_collection.sample_qty = template.sample_qty + sample_collection.company = company + if(template.sample_details): sample_collection.sample_details = "Test :" + (template.get("lab_test_name") or template.get("template")) +"\n"+"Collection Detials:\n\t"+template.sample_details sample_collection.save(ignore_permissions=True) @@ -229,7 +232,7 @@ def create_sample_doc(template, patient, invoice): def create_sample_collection(lab_test, template, patient, invoice): if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"): - sample_collection = create_sample_doc(template, patient, invoice) + sample_collection = create_sample_doc(template, patient, invoice, lab_test.company) if(sample_collection): lab_test.sample = sample_collection.name return lab_test From 38f0258991fc4e1a82aac0fc13dbc09068d4241c Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:32:35 +0530 Subject: [PATCH 033/410] feat: multi company support --- .../clinical_procedure/clinical_procedure.js | 35 ++++++++--- .../clinical_procedure.json | 58 ++++++++++++++++--- .../clinical_procedure/clinical_procedure.py | 2 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 5f36bdd95c6..213fa3de1f5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -175,16 +175,37 @@ frappe.ui.form.on('Clinical Procedure', { name: frm.doc.appointment }, callback: function(data) { - frm.set_value('patient', data.message.patient); - frm.set_value('procedure_template', data.message.procedure_template); - frm.set_value('medical_department', data.message.department); - frm.set_value('start_date', data.message.appointment_date); - frm.set_value('start_time', data.message.appointment_time); - frm.set_value('notes', data.message.notes); - frm.set_value('service_unit', data.message.service_unit); + let values = { + 'patient':data.message.patient, + 'procedure_template': data.message.procedure_template, + 'medical_department': data.message.department, + 'start_date': data.message.appointment_date, + 'start_time': data.message.appointment_time, + 'notes': data.message.notes, + 'service_unit': data.message.service_unit, + 'company': data.message.company + } + frm.set_value(values); } }); } + else{ + let values = { + 'patient': '', + 'patient_name': '', + 'patient_sex': '', + 'patient_age': '', + 'medical_department': '', + 'procedure_template': '', + 'start_date': '', + 'start_time': '', + 'notes': '', + 'service_unit': '', + 'inpatient_record': '' + // 'inpatient_status': '' + }; + frm.set_value(values); + } }, procedure_template: function(frm) { diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 3c936bbf277..59a87eccde4 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -7,16 +7,18 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "inpatient_record", "naming_series", - "procedure_template", "appointment", + "column_break_30", + "procedure_template", + "section_break_6", "patient", + "patient_name", "patient_sex", "patient_age", - "prescription", "medical_department", "practitioner", + "practitioner_name", "column_break_7", "status", "service_unit", @@ -26,7 +28,6 @@ "sample", "invoiced", "notes", - "company", "consumables_section", "consume_stock", "items", @@ -36,6 +37,11 @@ "consumable_total_amount", "column_break_27", "consumption_details", + "sb_refs", + "inpatient_record", + "company", + "column_break_34", + "prescription", "amended_from" ], "fields": [ @@ -56,7 +62,7 @@ { "fieldname": "appointment", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Appointment", "options": "Patient Appointment" }, @@ -64,7 +70,7 @@ "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", - "in_list_view": 1, + "in_standard_filter": 1, "label": "Patient", "options": "Patient", "reqd": 1 @@ -88,17 +94,20 @@ "fieldtype": "Link", "hidden": 1, "label": "Procedure Prescription", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "read_only": 1 }, { "fieldname": "medical_department", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Medical Department", "options": "Medical Department" }, { "fieldname": "practitioner", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Healthcare Practitioner", "options": "Healthcare Practitioner" }, @@ -237,11 +246,43 @@ { "fieldname": "section_break_24", "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "sb_refs", + "fieldtype": "Section Break" + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Practitioner Name", + "read_only": 1 + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-03-02 11:44:27.970651", + "modified": "2020-04-03 23:06:04.009856", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -263,5 +304,6 @@ "restrict_to_domain": "Healthcare", "sort_field": "modified", "sort_order": "DESC", + "title_field": "patient_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index db3afc8807e..56617e52586 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -37,7 +37,7 @@ class ClinicalProcedure(Document): template = frappe.get_doc('Clinical Procedure Template', self.procedure_template) if template.sample: patient = frappe.get_doc('Patient', self.patient) - sample_collection = create_sample_doc(template, patient, None) + sample_collection = create_sample_doc(template, patient, None, self.company) frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name) self.reload() From 7ad98c95b9af953b53c30bd641b74bd331f1bfc2 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:34:28 +0530 Subject: [PATCH 034/410] company field added --- .../inpatient_record/inpatient_record.json | 1165 ++++------------- 1 file changed, 220 insertions(+), 945 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index 92c11fbb4da..c1b516d5363 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -1,980 +1,255 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-07-11 17:48:51.404139", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2018-07-11 17:48:51.404139", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "naming_series", + "patient", + "patient_name", + "gender", + "blood_group", + "dob", + "mobile", + "email", + "phone", + "column_break_8", + "company", + "status", + "scheduled_date", + "admitted_datetime", + "expected_discharge", + "discharge_date", + "references", + "cb_admission", + "admission_practitioner", + "admission_encounter", + "cb_discharge", + "discharge_practitioner", + "discharge_encounter", + "sb_inpatient_occupancy", + "inpatient_occupancies", + "btn_transfer", + "sb_discharge_note", + "discharge_note" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, { - "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": 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": "Series", - "length": 0, - "no_copy": 0, - "options": "HLC-INP-.YYYY.-", - "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": "naming_series", + "fieldtype": "Select", + "hidden": 1, + "label": "Series", + "options": "HLC-INP-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "patient", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Patient", - "length": 0, - "no_copy": 0, - "options": "Patient", - "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": "patient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Patient", + "options": "Patient", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.patient_name", - "fieldname": "patient_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Name", - "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 - }, + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.sex", - "fieldname": "gender", - "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "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 - }, + "fetch_from": "patient.sex", + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.blood_group", - "fieldname": "blood_group", - "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": "Blood Group", - "length": 0, - "no_copy": 0, - "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", - "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 - }, + "fetch_from": "patient.blood_group", + "fieldname": "blood_group", + "fieldtype": "Select", + "label": "Blood Group", + "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dob", - "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 of birth", - "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 - }, + "fetch_from": "patient.dob", + "fieldname": "dob", + "fieldtype": "Date", + "label": "Date of birth", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.mobile", - "fieldname": "mobile", - "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": "Mobile", - "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 - }, + "fetch_from": "patient.mobile", + "fieldname": "mobile", + "fieldtype": "Data", + "label": "Mobile", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.email", - "fieldname": "email", - "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": "Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "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 - }, + "fetch_from": "patient.email", + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "patient.phone", - "fieldname": "phone", - "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": "Phone", - "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 - }, + "fetch_from": "patient.phone", + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "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_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "scheduled_date", - "fieldtype": "Date", - "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": "Admission Schedule 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 - }, + "default": "Today", + "fieldname": "scheduled_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Admission Schedule Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "admitted_datetime", - "fieldtype": "Datetime", - "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": "Admitted Datetime", - "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 - }, + "default": "Today", + "fieldname": "admitted_datetime", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Admitted Datetime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expected_discharge", - "fieldtype": "Date", - "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": "Expected Discharge", - "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": "expected_discharge", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expected Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_date", - "fieldtype": "Date", - "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": "Discharge 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "references", - "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": "References", - "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": "references", + "fieldtype": "Section Break", + "label": "References" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_admission", - "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, - "label": "Admission", - "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": "cb_admission", + "fieldtype": "Column Break", + "label": "Admission" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "admission_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_discharge", - "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, - "label": "Discharge", - "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": "cb_discharge", + "fieldtype": "Column Break", + "label": "Discharge" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_practitioner", - "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": "Healthcare Practitioner", - "length": 0, - "no_copy": 0, - "options": "Healthcare Practitioner", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_encounter", - "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": "Patient Encounter", - "length": 0, - "no_copy": 0, - "options": "Patient Encounter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discharge_encounter", + "fieldtype": "Link", + "label": "Patient Encounter", + "options": "Patient Encounter", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_inpatient_occupancy", - "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": "Inpatient Occupancy", - "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": "sb_inpatient_occupancy", + "fieldtype": "Section Break", + "label": "Inpatient Occupancy" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inpatient_occupancies", - "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, - "length": 0, - "no_copy": 0, - "options": "Inpatient Occupancy", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inpatient_occupancies", + "fieldtype": "Table", + "options": "Inpatient Occupancy", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "btn_transfer", - "fieldtype": "Button", - "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": "Transfer", - "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": "btn_transfer", + "fieldtype": "Button", + "label": "Transfer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.status != \"Admission Scheduled\"", - "fieldname": "sb_discharge_note", - "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": "Discharge Note", - "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 - }, + "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "fieldname": "sb_discharge_note", + "fieldtype": "Section Break", + "label": "Discharge Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discharge_note", - "fieldtype": "Text Editor", - "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": "", - "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": "discharge_note", + "fieldtype": "Text Editor" + }, + { + "fetch_from": "admission_encounter.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:43.168245", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Inpatient Record", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-04-07 13:13:39.351977", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Inpatient Record", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "patient", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "patient", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Healthcare", + "search_fields": "patient", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient", + "track_changes": 1 +} \ No newline at end of file From 93d0c78ca5121eb6a0e23279cacacae07b18395e Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 13 Apr 2020 16:42:03 +0530 Subject: [PATCH 035/410] feat: multi-company billing sales onvoice - filter get items based on company utils - company filters in all get item helper methods utils - refactor appointemnt items --- .../doctype/sales_invoice/sales_invoice.js | 7 +- erpnext/healthcare/utils.py | 96 ++++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 6f78db2ccc3..7741842fca1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -919,7 +919,7 @@ var get_healthcare_services_to_invoice = function(frm) { if(patient && patient!=selected_patient){ selected_patient = patient; var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice"; - var args = {patient: patient}; + var args = {patient: patient, company: frm.doc.company}; var columns = (["service", "reference_name", "reference_type"]); get_healthcare_items(frm, true, $results, $placeholder, method, args, columns); } @@ -1063,7 +1063,10 @@ var get_drugs_to_invoice = function(frm) { description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', get_query: function(doc) { return { - filters: { patient: dialog.get_value("patient"), docstatus: 1 } + filters: { patient: dialog.get_value("patient"), + company: frm.doc.company, + docstatus: 1 + } }; } }, diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 246242ad84c..e9cc597e813 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -3,82 +3,84 @@ # For license information, please see license.txt from __future__ import unicode_literals +import math +import json import frappe from frappe import _ -import math from frappe.utils import time_diff_in_hours, rounded from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple @frappe.whitelist() -def get_healthcare_services_to_invoice(patient): +def get_healthcare_services_to_invoice(patient, company): patient = frappe.get_doc('Patient', patient) + items_to_invoice = [] if patient: validate_customer_created(patient) - items_to_invoice = [] - patient_appointments = frappe.get_list( - 'Patient Appointment', - fields='*', - filters={'patient': patient.name, 'invoiced': 0}, - order_by='appointment_date' - ) - if patient_appointments: - items_to_invoice = get_fee_validity(patient_appointments) + # Customer validated, build a list of billable services + items_to_invoice += get_appointments_to_invoice(patient, company) + items_to_invoice += get_encounters_to_invoice(patient, company) + items_to_invoice += get_lab_tests_to_invoice(patient, company) + items_to_invoice += get_clinical_procedures_to_invoice(patient, company) + items_to_invoice += get_inpatient_services_to_invoice(patient, company) - encounters = get_encounters_to_invoice(patient) - lab_tests = get_lab_tests_to_invoice(patient) - clinical_procedures = get_clinical_procedures_to_invoice(patient) - inpatient_services = get_inpatient_services_to_invoice(patient) - - items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services return items_to_invoice + def validate_customer_created(patient): if not frappe.db.get_value('Patient', patient.name, 'customer'): msg = _("Please set a Customer linked to the Patient") msg += " {0}".format(patient.name) frappe.throw(msg, title=_('Customer Not Found')) -def get_fee_validity(patient_appointments): - if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'): - return - items_to_invoice = [] +def get_appointments_to_invoice(patient, company): + appointments_to_invoice = [] + patient_appointments = frappe.get_list( + 'Patient Appointment', + fields = '*', + filters = {'patient': patient.name, 'company': company, 'invoiced': 0}, + order_by = 'appointment_date' + ) + for appointment in patient_appointments: + # Procedure Appointments if appointment.procedure_template: if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'): - items_to_invoice.append({ + appointments_to_invoice.append({ 'reference_type': 'Patient Appointment', 'reference_name': appointment.name, 'service': appointment.procedure_template }) + # Consultation Appointments, should check fee validity else: - fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}) - if not fee_validity: - practitioner_charge = 0 - income_account = None - service_item = None - if appointment.practitioner: - service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) - income_account = get_income_account(appointment.practitioner, appointment.company) - items_to_invoice.append({ - 'reference_type': 'Patient Appointment', - 'reference_name': appointment.name, - 'service': service_item, - 'rate': practitioner_charge, - 'income_account': income_account - }) + if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \ + frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}): + continue # Skip invoicing, fee validty present + practitioner_charge = 0 + income_account = None + service_item = None + if appointment.practitioner: + service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment) + income_account = get_income_account(appointment.practitioner, appointment.company) + appointments_to_invoice.append({ + 'reference_type': 'Patient Appointment', + 'reference_name': appointment.name, + 'service': service_item, + 'rate': practitioner_charge, + 'income_account': income_account + }) - return items_to_invoice + return appointments_to_invoice -def get_encounters_to_invoice(patient): +def get_encounters_to_invoice(patient, company): encounters_to_invoice = [] encounters = frappe.get_list( 'Patient Encounter', fields=['*'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) if encounters: for encounter in encounters: @@ -101,12 +103,12 @@ def get_encounters_to_invoice(patient): return encounters_to_invoice -def get_lab_tests_to_invoice(patient): +def get_lab_tests_to_invoice(patient, company): lab_tests_to_invoice = [] lab_tests = frappe.get_list( 'Lab Test', fields=['name', 'template'], - filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1} + filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1} ) for lab_test in lab_tests: item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.lab_test_code, ['item', 'is_billable']) @@ -142,12 +144,12 @@ def get_lab_tests_to_invoice(patient): return lab_tests_to_invoice -def get_clinical_procedures_to_invoice(patient): +def get_clinical_procedures_to_invoice(patient, company): clinical_procedures_to_invoice = [] procedures = frappe.get_list( 'Clinical Procedure', fields='*', - filters={'patient': patient.name, 'invoiced': False} + filters={'patient': patient.name, 'company': company, 'invoiced': False} ) for procedure in procedures: if not procedure.appointment: @@ -203,7 +205,7 @@ def get_clinical_procedures_to_invoice(patient): return clinical_procedures_to_invoice -def get_inpatient_services_to_invoice(patient): +def get_inpatient_services_to_invoice(patient, company): services_to_invoice = [] inpatient_services = frappe.db.sql( ''' @@ -213,10 +215,11 @@ def get_inpatient_services_to_invoice(patient): `tabInpatient Record` ip, `tabInpatient Occupancy` io WHERE ip.patient=%s + and ip.company=%s and io.parent=ip.name and io.left=1 and io.invoiced=0 - ''', (patient.name), as_dict=1) + ''', (patient.name, company), as_dict=1) for inpatient_occupancy in inpatient_services: service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type') @@ -376,6 +379,7 @@ def check_fee_validity(appointment): def manage_fee_validity(appointment): fee_validity = check_fee_validity(appointment) + if fee_validity: if appointment.status == 'Cancelled' and fee_validity.visited > 0: fee_validity.visited -= 1 From b922b292eb31454ffd15c2efa135e534f52b9bee Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 16 Apr 2020 12:52:03 +0530 Subject: [PATCH 036/410] fix: set patient while billing, tests fixed --- .../patient_appointment.py | 2 +- .../test_patient_appointment.py | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 3786bf28619..cc3492a810f 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -119,6 +119,7 @@ def invoice_appointment(appointment_doc): if automate_invoicing and not appointment_invoiced and not fee_validity: sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.patient = appointment_doc.patient sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() @@ -339,7 +340,6 @@ def make_encounter(source_name, target_doc=None): ['practitioner', 'practitioner'], ['medical_department', 'department'], ['patient_sex', 'patient_sex'], - ['encounter_date', 'appointment_date'], ['invoiced', 'invoiced'], ['company', 'company'] ] diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 7075af5d00c..7cdb28e0da1 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter from frappe.utils import nowdate, add_days from frappe.utils.make_random import get_random @@ -23,6 +23,14 @@ class TestPatientAppointment(unittest.TestCase): create_encounter(appointment) self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed') + def test_start_encounter(self): + patient, medical_department, practitioner = create_healthcare_docs() + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 3)) + encounter = create_encounter(appointment) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'company'), appointment.company) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'practitioner'), appointment.practitioner) + self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'patient'), appointment.patient) + def test_invoicing(self): patient, medical_department, practitioner = create_healthcare_docs() frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0) @@ -33,7 +41,11 @@ class TestPatientAppointment(unittest.TestCase): frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1) self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) - self.assertTrue(frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice')) + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertTrue(sales_invoice_name) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'company'), appointment.company) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient) + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount) def test_appointment_cancel(self): patient, medical_department, practitioner = create_healthcare_docs() @@ -53,8 +65,8 @@ class TestPatientAppointment(unittest.TestCase): appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) update_status(appointment.name, 'Cancelled') # check invoice cancelled - sales_invoice = frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice') - self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice, 'status'), 'Cancelled') + sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent') + self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled') def create_healthcare_docs(): @@ -90,14 +102,9 @@ def create_patient(): patient = patient.name return patient -def create_encounter(appointment=None): - encounter = frappe.new_doc('Patient Encounter') +def create_encounter(appointment): if appointment: - encounter.appointment = appointment.name - encounter.patient = appointment.patient - encounter.practitioner = appointment.practitioner - encounter.encounter_date = appointment.appointment_date - encounter.encounter_time = appointment.appointment_time + encounter = make_encounter(appointment.name) encounter.save() encounter.submit() return encounter From 1c49a12ac29ef1bdf1fd95f6bc27710f7015eb63 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 16 Apr 2020 17:00:47 +0530 Subject: [PATCH 037/410] fix: patient appointment auto invoicing: record payment if payment mode and amount available test: fixes --- .../patient_appointment.py | 12 +++++---- .../test_patient_appointment.py | 26 ++++++++++++++----- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index cc3492a810f..fcd87d719b8 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -123,22 +123,24 @@ def invoice_appointment(appointment_doc): sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() - sales_invoice.is_pos = 1 sales_invoice.company = appointment_doc.company sales_invoice.debit_to = get_receivable_account(appointment_doc.company) item = sales_invoice.append('items', {}) item = get_appointment_item(appointment_doc, item) - payment = sales_invoice.append('payments', {}) - payment.mode_of_payment = appointment_doc.mode_of_payment - payment.amount = appointment_doc.paid_amount + # Add payments if payment details are supplied else proceed to create invoice as Unpaid + if appointment_doc.mode_of_payment and appointment_doc.paid_amount: + sales_invoice.is_pos = 1 + payment = sales_invoice.append('payments', {}) + payment.mode_of_payment = appointment_doc.mode_of_payment + payment.amount = appointment_doc.paid_amount sales_invoice.set_missing_values(for_validate=True) sales_invoice.flags.ignore_mandatory = True sales_invoice.save(ignore_permissions=True) sales_invoice.submit() - frappe.msgprint(_('Sales Invoice {0} created as paid'.format(sales_invoice.name)), alert=True) + frappe.msgprint(_('Sales Invoice {0} created'.format(sales_invoice.name)), alert=True) frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1) frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name) diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 7cdb28e0da1..eeed157291e 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -11,7 +11,8 @@ from frappe.utils.make_random import get_random class TestPatientAppointment(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabPatient Appointment`""") - frappe.db.sql("""delete from `tabFee Validity""") + frappe.db.sql("""delete from `tabFee Validity`""") + frappe.db.sql("""delete from `tabPatient Encounter`""") def test_status(self): patient, medical_department, practitioner = create_healthcare_docs() @@ -25,11 +26,16 @@ class TestPatientAppointment(unittest.TestCase): def test_start_encounter(self): patient, medical_department, practitioner = create_healthcare_docs() - appointment = create_appointment(patient, practitioner, add_days(nowdate(), 3)) - encounter = create_encounter(appointment) - self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'company'), appointment.company) - self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'practitioner'), appointment.practitioner) - self.assertEquals(frappe.db.get_value('Patient Encounter', encounter.name, 'patient'), appointment.patient) + frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) + appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1) + self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1) + encounter = make_encounter(appointment.name) + self.assertTrue(encounter) + self.assertEqual(encounter.company, appointment.company) + self.assertEqual(encounter.practitioner, appointment.practitioner) + self.assertEqual(encounter.patient, appointment.patient) + # invoiced flag mapped from appointment + self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced')) def test_invoicing(self): patient, medical_department, practitioner = create_healthcare_docs() @@ -104,7 +110,13 @@ def create_patient(): def create_encounter(appointment): if appointment: - encounter = make_encounter(appointment.name) + encounter = frappe.new_doc('Patient Encounter') + encounter.appointment = appointment.name + encounter.patient = appointment.patient + encounter.practitioner = appointment.practitioner + encounter.encounter_date = appointment.appointment_date + encounter.encounter_time = appointment.appointment_time + encounter.company = appointment.company encounter.save() encounter.submit() return encounter From fdc075220d6247089e3f108d5937a6f681580aa1 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 16 Apr 2020 23:05:38 +0530 Subject: [PATCH 038/410] fix: minor, codacy suggestions --- .../doctype/clinical_procedure/clinical_procedure.js | 3 +-- erpnext/healthcare/utils.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 213fa3de1f5..80157e1c251 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -184,7 +184,7 @@ frappe.ui.form.on('Clinical Procedure', { 'notes': data.message.notes, 'service_unit': data.message.service_unit, 'company': data.message.company - } + }; frm.set_value(values); } }); @@ -202,7 +202,6 @@ frappe.ui.form.on('Clinical Procedure', { 'notes': '', 'service_unit': '', 'inpatient_record': '' - // 'inpatient_status': '' }; frm.set_value(values); } diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index e9cc597e813..17d8f642928 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import math -import json import frappe from frappe import _ from frappe.utils import time_diff_in_hours, rounded From ec7db4f4679855890eccaec643be4d1f57bde9be Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 23 Apr 2020 03:10:11 +0530 Subject: [PATCH 039/410] fix: modified timestamp conflict --- .../doctype/patient_appointment/patient_appointment.json | 2 +- .../healthcare/doctype/patient_encounter/patient_encounter.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 9297588509c..6e889c2a0ce 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -328,7 +328,7 @@ } ], "links": [], - "modified": "2020-04-07 11:16:34.981240", + "modified": "2020-04-23 11:16:34.981240", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 12203fde8d7..ff7ecc926f3 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -328,7 +328,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-03 23:06:16.348846", + "modified": "2020-04-23 23:06:16.348846", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", From c02eceb989c5e4647684858ac753887094192f84 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 23 Apr 2020 21:15:06 +0530 Subject: [PATCH 040/410] style: intendation --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7741842fca1..9aaee3cf625 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -1063,10 +1063,11 @@ var get_drugs_to_invoice = function(frm) { description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.', get_query: function(doc) { return { - filters: { patient: dialog.get_value("patient"), - company: frm.doc.company, - docstatus: 1 - } + filters: { + patient: dialog.get_value("patient"), + company: frm.doc.company, + docstatus: 1 + } }; } }, From defeb737471efab0100df5682777572ed0c4e2ee Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 24 Apr 2020 04:07:14 +0530 Subject: [PATCH 041/410] feat: Add Failed error logs fix: set defaults --- .../tally_migration/tally_migration.json | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index dc6f093ac9d..005c8a98c6b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -28,14 +28,17 @@ "vouchers", "accounts_section", "default_warehouse", - "round_off_account", + "default_round_off_account", "column_break_21", "default_cost_center", "day_book_section", "day_book_data", "column_break_27", "is_day_book_data_processed", - "is_day_book_data_imported" + "is_day_book_data_imported", + "import_log_section", + "failed_import_log", + "failed_import_preview" ], "fields": [ { @@ -57,6 +60,7 @@ "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -69,6 +73,7 @@ "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -92,7 +97,7 @@ "fieldname": "erpnext_company", "fieldtype": "Data", "label": "ERPNext Company", - "read_only_depends_on": "eval:doc.is_master_data_processed == 1" + "read_only_depends_on": "eval:doc.is_master_data_processed==1" }, { "fieldname": "processed_files_section", @@ -136,6 +141,7 @@ }, { "depends_on": "is_master_data_imported", + "description": "The accounts are set by the system automatically but do confirm these defaults", "fieldname": "accounts_section", "fieldtype": "Section Break", "label": "Accounts" @@ -146,12 +152,6 @@ "label": "Default Warehouse", "options": "Warehouse" }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, { "fieldname": "column_break_21", "fieldtype": "Column Break" @@ -212,11 +212,36 @@ "fieldname": "default_uom", "fieldtype": "Link", "label": "Default UOM", - "options": "UOM" + "options": "UOM", + "read_only_depends_on": "eval:doc.is_master_data_imported==1" + }, + { + "default": "[]", + "fieldname": "failed_import_log", + "fieldtype": "Code", + "hidden": 1, + "label": "Failed Import Log", + "options": "JSON" + }, + { + "fieldname": "failed_import_preview", + "fieldtype": "HTML", + "label": "Failed Import Log" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "default_round_off_account", + "fieldtype": "Link", + "label": "Default Round Off Account", + "options": "Account" } ], "links": [], - "modified": "2020-04-16 13:03:28.894919", + "modified": "2020-04-22 16:01:13.718842", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 2de4a5a7c884080db1ac03acf40262ca887f8913 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 11:34:00 +0530 Subject: [PATCH 042/410] feat: Error logging via doc fix: Handle duplicate company and COA feat: Error logging via doc feat: fetch defaults for accounts --- .../tally_migration/tally_migration.py | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 13474e19ee1..32d11681fb0 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import json import re +import sys import traceback import zipfile from decimal import Decimal @@ -15,13 +16,14 @@ from bs4 import BeautifulSoup as bs import frappe from erpnext import encode_company_abbr from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts +from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data + from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime - PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 @@ -65,9 +67,17 @@ class TallyMigration(Document): "attached_to_name": self.name, "content": json.dumps(value), "is_private": True - }).insert() + }) + try: + f.insert() + except frappe.DuplicateEntryError: + pass setattr(self, key, f.file_url) + def set_account_defaults(self): + self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"]) + self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse") + def _process_master_data(self): def get_company_name(collection): return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() @@ -84,7 +94,11 @@ class TallyMigration(Document): children, parents = get_children_and_parent_dict(accounts) group_set = [acc[1] for acc in accounts if acc[2]] children, customers, suppliers = remove_parties(parents, children, group_set) - coa = traverse({}, children, roots, roots, group_set) + + try: + coa = traverse({}, children, roots, roots, group_set) + except RecursionError: + self.log() for account in coa: coa[account]["root_type"] = root_type_map[account] @@ -242,12 +256,18 @@ class TallyMigration(Document): def create_company_and_coa(coa_file_url): coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) frappe.local.flags.ignore_chart_of_accounts = True - company = frappe.get_doc({ - "doctype": "Company", - "company_name": self.erpnext_company, - "default_currency": "INR", - "enable_perpetual_inventory": 0, - }).insert() + + try: + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + except frappe.DuplicateEntryError: + company = frappe.get_doc("Company", self.erpnext_company) + unset_existing_data(self.erpnext_company) + frappe.local.flags.ignore_chart_of_accounts = False create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) company.create_default_warehouses() @@ -256,36 +276,35 @@ class TallyMigration(Document): parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) for party in json.loads(parties_file.get_content()): try: - frappe.get_doc(party).insert() + party_doc = frappe.get_doc(party) + party_doc.insert() except: - self.log(party) + self.log(party_doc) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) for address in json.loads(addresses_file.get_content()): try: - frappe.get_doc(address).insert(ignore_mandatory=True) + address_doc = frappe.get_doc(address) + address_doc.insert(ignore_mandatory=True) except: - try: - gstin = address.pop("gstin", None) - frappe.get_doc(address).insert(ignore_mandatory=True) - self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) - except: - self.log(address) + self.log(address_doc) def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): if not frappe.db.exists(uom): try: - frappe.get_doc(uom).insert() + uom_doc = frappe.get_doc(uom) + uom_doc.insert() except: - self.log(uom) + self.log(uom_doc) items_file = frappe.get_doc("File", {"file_url": items_file_url}) for item in json.loads(items_file.get_content()): try: - frappe.get_doc(item).insert() + item_doc = frappe.get_doc(item) + item_doc.insert() except: - self.log(item) + self.log(item_doc) try: self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) @@ -299,6 +318,7 @@ class TallyMigration(Document): self.publish("Import Master Data", _("Done"), 4, 4) + self.set_account_defaults() self.is_master_data_imported = 1 except: @@ -468,13 +488,13 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): + df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } for doctype in doctypes: - df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID" - } create_custom_field(doctype, df) def create_price_list(): @@ -521,11 +541,12 @@ class TallyMigration(Document): for index, voucher in enumerate(chunk, start=start): try: - doc = frappe.get_doc(voucher).insert() - doc.submit() + voucher_doc = frappe.get_doc(voucher) + voucher_doc.insert() + voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) except: - self.log(voucher) + self.log(voucher_doc) if is_last: self.status = "" @@ -551,9 +572,19 @@ class TallyMigration(Document): frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - data = data or self.status - message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) - return frappe.log_error(title="Tally Migration Error", message=message) + if isinstance(data, frappe.model.document.Document): + if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: + failed_import_log = json.loads(self.failed_import_log) + failed_import_log.append({ + "doc": data.as_dict(), + "exc": traceback.format_exc() + }) + self.failed_import_log = json.dumps(failed_import_log) + self.save() + else: + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) def set_status(self, status=""): self.status = status From 38c677689dd54d91e0255700d94a68b0e92b3c1e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 23 Apr 2020 14:07:14 +0530 Subject: [PATCH 043/410] fix: set default round off acount --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 32d11681fb0..5a5759160b5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -510,7 +510,7 @@ class TallyMigration(Document): try: frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers = json.loads(vouchers_file.get_content()) From 82e7a9de2856a27ed497d7559fe775524a1c54e0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:12:38 +0530 Subject: [PATCH 044/410] feat: error log handling fix: toggle required accounts --- .../tally_migration/tally_migration.js | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d84c8234efa..d06fe537ad1 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tally Migration', { +frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { @@ -36,6 +36,10 @@ frappe.ui.form.on('Tally Migration', { }); }, refresh: function (frm) { + frm.trigger("show_import_log"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { + frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -71,5 +75,101 @@ frappe.ui.form.on('Tally Migration', { frm.reload_doc(); } ); + }, + show_import_log(frm) { + let index = 0; + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.toggle_display("import_log_section", logs.length > 0); + + + const getError = (traceback) => { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + + return error_line.slice(split_str_idx).trim(); + } + + const cleanDoc = (obj) => { + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; + }; + + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let html = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${html}
+
${show_doc}
+ + +
${create_button}
+ + `; + }) + .join(""); + + frm.get_field("failed_import_preview").$wrapper.html(` + + + + + + + + ${rows} + + + +
${__("#")}${__("DocType")}${__("Error Message")}${__("Create")}
And ${hidden_logs.length} more others
+ `); } }); From 0805307514a7c9cafa6af9d6b5e0a490d42c5800 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:42:00 +0530 Subject: [PATCH 045/410] perf: reduce failed_error_log size --- .../doctype/tally_migration/tally_migration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 5a5759160b5..46382c1e8f5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -575,11 +575,14 @@ class TallyMigration(Document): if isinstance(data, frappe.model.document.Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) + doc = data.as_dict() + doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } + stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": data.as_dict(), + "doc": stripped_doc, "exc": traceback.format_exc() }) - self.failed_import_log = json.dumps(failed_import_log) + self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() else: data = data or self.status From e30cc8359146ad0bcf4cadce37a61bf6850b852a Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 27 Apr 2020 19:36:49 +0530 Subject: [PATCH 046/410] fix: field order, minor fixes - patient - update customer, releated fields patient appointment - send message --- .../clinical_procedure/clinical_procedure.js | 20 +++---- .../clinical_procedure.json | 44 +++++++++++----- .../healthcare/doctype/lab_test/lab_test.js | 8 +-- .../healthcare/doctype/lab_test/lab_test.py | 4 +- erpnext/healthcare/doctype/patient/patient.js | 2 + .../healthcare/doctype/patient/patient.json | 11 ++-- erpnext/healthcare/doctype/patient/patient.py | 49 +++++++++++++---- .../patient_appointment.js | 5 ++ .../patient_appointment.json | 52 ++++++++++--------- .../patient_appointment.py | 12 +++-- .../patient_encounter/patient_encounter.js | 31 +++++++---- .../patient_encounter/patient_encounter.json | 41 +++++---------- 12 files changed, 168 insertions(+), 111 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 80157e1c251..2d30e9003bc 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -87,8 +87,8 @@ frappe.ui.form.on('Clinical Procedure', { ['' + r.message + '']), indicator: 'green' }); - frm.reload_doc(); } + frm.reload_doc(); } }); } @@ -156,11 +156,13 @@ frappe.ui.form.on('Clinical Procedure', { age = __('{0} as on {1}', [age, data.message.age_as_on]); } } + frm.set_value('patient_name', data.message.patient_name); frm.set_value('patient_age', age); frm.set_value('patient_sex', data.message.sex); } }); } else { + frm.set_value('patient_name', ''); frm.set_value('patient_age', ''); frm.set_value('patient_sex', ''); } @@ -179,6 +181,7 @@ frappe.ui.form.on('Clinical Procedure', { 'patient':data.message.patient, 'procedure_template': data.message.procedure_template, 'medical_department': data.message.department, + 'practitioner': data.message.practitioner, 'start_date': data.message.appointment_date, 'start_time': data.message.appointment_time, 'notes': data.message.notes, @@ -188,8 +191,7 @@ frappe.ui.form.on('Clinical Procedure', { frm.set_value(values); } }); - } - else{ + } else { let values = { 'patient': '', 'patient_name': '', @@ -252,9 +254,11 @@ frappe.ui.form.on('Clinical Procedure', { name: frm.doc.practitioner }, callback: function (data) { - frappe.model.set_value(frm.doctype,frm.docname, 'medical_department',data.message.department); + frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', data.message.practitioner_name); } }); + } else { + frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', ''); } }, @@ -302,14 +306,6 @@ frappe.ui.form.on('Clinical Procedure', { }); -cur_frm.set_query('procedure_template', function(doc) { - return { - filters: { - 'medical_department': doc.medical_department - } - }; -}); - frappe.ui.form.on('Clinical Procedure Item', { qty: function(frm, cdt, cdn) { let d = locals[cdt][cdn]; diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 59a87eccde4..7c4b9a3ed3a 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -9,27 +9,29 @@ "field_order": [ "naming_series", "appointment", - "column_break_30", "procedure_template", + "column_break_30", + "company", + "invoiced", "section_break_6", "patient", "patient_name", "patient_sex", "patient_age", - "medical_department", - "practitioner", - "practitioner_name", + "inpatient_record", + "notes", "column_break_7", "status", + "practitioner", + "practitioner_name", + "medical_department", "service_unit", - "warehouse", "start_date", "start_time", "sample", - "invoiced", - "notes", "consumables_section", "consume_stock", + "warehouse", "items", "section_break_24", "invoice_separately_as_consumables", @@ -38,8 +40,6 @@ "column_break_27", "consumption_details", "sb_refs", - "inpatient_record", - "company", "column_break_34", "prescription", "amended_from" @@ -64,10 +64,10 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Appointment", - "options": "Patient Appointment" + "options": "Patient Appointment", + "set_only_once": 1 }, { - "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", "in_standard_filter": 1, @@ -217,6 +217,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.__islocal", "fieldname": "status", "fieldtype": "Select", "in_list_view": 1, @@ -235,6 +236,8 @@ "read_only": 1 }, { + "collapsible": 1, + "collapsible_depends_on": "consume_stock", "fieldname": "consumables_section", "fieldtype": "Section Break", "label": "Consumables" @@ -261,14 +264,12 @@ "fieldtype": "Section Break" }, { - "fetch_from": "patient.patient_name", "fieldname": "patient_name", "fieldtype": "Data", "label": "Patient Name", "read_only": 1 }, { - "fetch_from": "practitioner.practitioner_name", "fieldname": "practitioner_name", "fieldtype": "Data", "in_list_view": 1, @@ -282,7 +283,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-03 23:06:04.009856", + "modified": "2020-04-24 22:53:13.156901", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -298,6 +299,21 @@ "report": 1, "role": "Nursing User", "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index e7e44dcb481..bf1ecc87e4c 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -137,13 +137,13 @@ var get_lab_test_prescribed = function(frm){ }); } else{ - frappe.msgprint(__("Please select a Patient to get Lab Orders")); + frappe.msgprint(__("Please select a Patient to get Lab Tests")); } }; var show_lab_tests = function(frm, result){ var d = new frappe.ui.Dialog({ - title: __("Lab Test Prescriptions"), + title: __("Lab Tests"), fields: [ { fieldtype: "HTML", fieldname: "lab_test" @@ -161,7 +161,7 @@ var show_lab_tests = function(frm, result){ ', {name:y[0], lab_test: y[1], encounter:y[2], invoiced:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field); row.find("a").click(function() { frm.doc.template = $(this).attr("data-lab-test"); @@ -181,7 +181,7 @@ var show_lab_tests = function(frm, result){ }); }); if(!result.length){ - var msg = "No Lab Orders found for patient " + frm.doc.patient_name + ""; + var msg = __("No Lab Tests found for the Patient {0}", [frm.doc.patient_name.bold()]); html_field.empty(); $(repl('
%(msg)s
', {msg: msg})).appendTo(html_field); } diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index e49429ea625..298d2939cf7 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -69,7 +69,7 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_("Lab Test(s) " + lab_test_created + " created.")) + frappe.msgprint(_("Lab Test(s) {0} created".format(lab_test_created))) else: frappe.msgprint(_("No Lab Tests created")) @@ -111,7 +111,7 @@ def create_lab_test_from_invoice(invoice_name): if lab_test_created != 1: template = get_lab_test_template(item.item_code) if template: - lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, company, invoice.company) + lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, invoice.company) if item.reference_dt == "Lab Prescription": lab_test.prescription = item.reference_dn lab_test.save(ignore_permissions = True) diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index a11faf58066..490f2475001 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -10,6 +10,8 @@ frappe.ui.form.on('Patient', { ] }; }); + frm.set_query('customer_group', {'is_group': 0}); + frm.set_query('default_price_list', { 'selling': 1}); if (frappe.defaults.get_default('patient_name_by') != 'Naming Series') { frm.toggle_display('naming_series', false); diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json index 2c701fbf944..8af1a9ccd75 100644 --- a/erpnext/healthcare/doctype/patient/patient.json +++ b/erpnext/healthcare/doctype/patient/patient.json @@ -72,6 +72,7 @@ { "fieldname": "inpatient_status", "fieldtype": "Select", + "in_preview": 1, "label": "Inpatient Status", "options": "\nAdmission Scheduled\nAdmitted\nDischarge Scheduled", "read_only": 1 @@ -106,6 +107,7 @@ { "fieldname": "sex", "fieldtype": "Link", + "in_preview": 1, "label": "Gender", "options": "Gender", "reqd": 1 @@ -114,6 +116,7 @@ "bold": 1, "fieldname": "blood_group", "fieldtype": "Select", + "in_preview": 1, "label": "Blood Group", "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative" }, @@ -121,6 +124,7 @@ "bold": 1, "fieldname": "dob", "fieldtype": "Date", + "in_preview": 1, "label": "Date of birth" }, { @@ -147,6 +151,7 @@ "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, + "in_preview": 1, "label": "Image", "no_copy": 1, "print_hide": 1, @@ -161,9 +166,9 @@ "fieldname": "customer", "fieldtype": "Link", "ignore_user_permissions": 1, - "in_preview": 1, "label": "Customer", - "options": "Customer" + "options": "Customer", + "set_only_once": 1 }, { "fieldname": "report_preference", @@ -390,7 +395,7 @@ "image_field": "image", "links": [], "max_attachments": 50, - "modified": "2020-04-11 14:53:48.767245", + "modified": "2020-04-25 17:24:32.146415", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient", diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index f83fac03a63..6349aece36a 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -10,6 +10,7 @@ from frappe.utils import cint, cstr, getdate import dateutil from frappe.model.naming import set_name_by_naming_series from frappe.utils.nestedset import get_root_of +from erpnext import get_default_currency from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms class Patient(Document): @@ -17,6 +18,9 @@ class Patient(Document): self.set_full_name() self.add_as_website_user() + def before_insert(self): + self.set_missing_customer_details() + def after_insert(self): self.add_as_website_user() self.reload() @@ -28,12 +32,44 @@ class Patient(Document): send_registration_sms(self) self.reload() # self.notify_update() + def on_update(self): + if self.customer: + customer = frappe.get_doc('Customer', self.customer) + if self.customer_group: + customer.customer_group = self.customer_group + if self.territory: + customer.territory = self.territory + + customer.default_price_list = self.default_price_list + customer.default_currency = self.default_currency + customer.language = self.language + customer.save(ignore_permissions=True) + else: + if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'): + create_customer(self) + def set_full_name(self): if self.last_name: self.patient_name = ' '.join(filter(None, [self.first_name, self.last_name])) else: self.patient_name = self.first_name + def set_missing_customer_details(self): + if not self.customer_group: + self.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') or get_root_of('Customer Group') + if not self.territory: + self.territory = frappe.db.get_single_value('Selling Settings', 'territory') or get_root_of('Territory') + if not self.default_price_list: + self.default_price_list = frappe.db.get_single_value('Selling Settings', 'selling_price_list') + + if not self.customer_group or not self.territory or not self.default_price_list: + frappe.msgprint(_('Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings'), alert=True) + + if not self.default_currency: + self.default_currency = get_default_currency() + if not self.language: + self.language = frappe.db.get_single_value('System Settings', 'language') + def add_as_website_user(self): if self.email: if not frappe.db.exists ('User', self.email): @@ -87,21 +123,14 @@ class Patient(Document): return {'invoice': sales_invoice.name} def create_customer(doc): - customer_group = doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group') - territory = doc.territory or frappe.db.get_single_value('Selling Settings', 'territory') - if not (customer_group and territory): - customer_group = get_root_of('Customer Group') - territory = get_root_of('Territory') - frappe.msgprint(_('Please set default customer group and territory in Selling Settings'), alert=True) - customer = frappe.get_doc({ 'doctype': 'Customer', 'customer_name': doc.patient_name, - 'customer_group': customer_group, - 'territory' : territory, + 'customer_group': doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group'), + 'territory' : doc.territory or frappe.db.get_single_value('Selling Settings', 'territory'), 'customer_type': 'Individual', 'default_currency': doc.default_currency, - 'default_price_ist': doc.default_price_list, + 'default_price_list': doc.default_price_list, 'language': doc.language }).insert(ignore_permissions=True, ignore_mandatory=True) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 28db7ffea4b..488abc21075 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -127,6 +127,11 @@ frappe.ui.form.on('Patient Appointment', { patient: function(frm) { if (frm.doc.patient) { frm.trigger('toggle_payment_fields'); + } else { + frm.set_value('patient_name', ''); + frm.set_value('patient_sex', ''); + frm.set_value('patient_age', ''); + frm.set_value('inpatient_record', ''); } }, diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 6e889c2a0ce..2b0bf216a92 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -10,33 +10,30 @@ "engine": "InnoDB", "field_order": [ "naming_series", + "status", "patient", "patient_name", "patient_sex", "patient_age", - "column_break_1", "inpatient_record", + "column_break_1", "company", "service_unit", - "status", - "section_break_11", - "get_procedure_from_encounter", - "column_break_13", "procedure_template", + "get_procedure_from_encounter", "procedure_prescription", "therapy_type", "get_prescribed_therapies", "therapy_plan", - "service_unit", - "section_break_12", "practitioner", "department", + "section_break_12", "appointment_type", + "duration", "column_break_17", "appointment_date", "appointment_time", "appointment_datetime", - "duration", "section_break_16", "mode_of_payment", "billing_item", @@ -45,9 +42,10 @@ "invoiced", "ref_sales_invoice", "section_break_3", - "notes", "referring_practitioner", - "reminded" + "reminded", + "column_break_36", + "notes" ], "fields": [ { @@ -59,7 +57,6 @@ "read_only": 1 }, { - "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -83,7 +80,8 @@ "fieldname": "duration", "fieldtype": "Int", "in_filter": 1, - "label": "Duration (In Minutes)" + "label": "Duration (In Minutes)", + "set_only_once": 1 }, { "fieldname": "column_break_1", @@ -102,6 +100,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.patient;", "fieldname": "procedure_template", "fieldtype": "Link", "label": "Clinical Procedure Template", @@ -133,7 +132,8 @@ }, { "fieldname": "section_break_12", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Appointment Details" }, { "fieldname": "practitioner", @@ -179,11 +179,13 @@ "fieldtype": "Time", "in_list_view": 1, "label": "Time", - "read_only": 1 + "read_only": 1, + "reqd": 1 }, { "fieldname": "section_break_16", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Payments" }, { "fetch_from": "patient.patient_name", @@ -212,6 +214,7 @@ { "fieldname": "appointment_datetime", "fieldtype": "Datetime", + "hidden": 1, "label": "Appointment Datetime", "print_hide": 1, "read_only": 1, @@ -243,9 +246,12 @@ { "fieldname": "company", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Company", "no_copy": 1, - "options": "Company" + "options": "Company", + "reqd": 1, + "set_only_once": 1 }, { "collapsible": 1, @@ -311,24 +317,20 @@ "options": "HLC-APP-.YYYY.-", "set_only_once": 1 }, - { - "fieldname": "section_break_11", - "fieldtype": "Section Break" - }, - { - "fieldname": "column_break_13", - "fieldtype": "Column Break" - }, { "fieldname": "billing_item", "fieldtype": "Link", "label": "Billing Item", "options": "Item", "read_only": 1 + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" } ], "links": [], - "modified": "2020-04-23 11:16:34.981240", + "modified": "2020-04-25 17:23:49.841975", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 67fd82dbc2d..a0e3de6b48d 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -369,17 +369,19 @@ def send_appointment_reminder(): frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1) def send_message(doc, message): - patient = frappe.get_doc('Patient', doc.patient) - if patient.mobile: + patient_mobile = frappe.db.get_value("Patient", doc.patient, "mobile") + if patient_mobile: context = {'doc': doc, 'alert': doc, 'comments': None} if doc.get('_comments'): context['comments'] = json.loads(doc.get('_comments')) # jinja to string convertion happens here message = frappe.render_template(message, context) - number = [patient.mobile] - send_sms(number, message) - + number = [patient_mobile] + try: + send_sms(number, message) + except Exception as e: + frappe.msgprint(_("SMS not sent, please check SMS Settings"), alert=True) @frappe.whitelist() def get_events(start, end, filters=None): diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 7600900495f..b2911d421eb 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -25,15 +25,16 @@ frappe.ui.form.on('Patient Encounter', { refresh_field('lab_test_prescription'); if (!frm.doc.__islocal) { - - if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') { - frm.add_custom_button(__('Schedule Discharge'), function() { - schedule_discharge(frm); - }); - } else if (frm.doc.inpatient_status != 'Discharge Scheduled') { - frm.add_custom_button(__('Schedule Admission'), function() { - schedule_inpatient(frm); - }); + if (frm.doc.docstatus == 1) { + if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') { + frm.add_custom_button(__('Schedule Discharge'), function() { + schedule_discharge(frm); + }); + } else if (frm.doc.inpatient_status != 'Discharge Scheduled') { + frm.add_custom_button(__('Schedule Admission'), function() { + schedule_inpatient(frm); + }); + } } frm.add_custom_button(__('Patient History'), function() { @@ -101,6 +102,11 @@ frappe.ui.form.on('Patient Encounter', { frm.events.set_patient_info(frm); }, + practitioner: function(frm) { + if(!frm.doc.practitioner) { + frm.set_value('practitioner_name', ''); + } + }, set_appointment_fields: function(frm) { if (frm.doc.appointment) { frappe.call({ @@ -118,6 +124,7 @@ frappe.ui.form.on('Patient Encounter', { 'company': data.message.company }; frm.set_value(values); + frm.set_df_property('patient', 'read_only', 1); } }); } @@ -134,6 +141,7 @@ frappe.ui.form.on('Patient Encounter', { 'inpatient_status': '' }; frm.set_value(values); + frm.set_df_property('patient', 'read_only', 0); } }, @@ -149,15 +157,20 @@ frappe.ui.form.on('Patient Encounter', { if (data.message.dob) { age = calculate_age(data.message.dob); } + frappe.model.set_value(frm.doctype, frm.docname, 'patient_mame', data.message.patient_mame); frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age); frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', data.message.sex); if (data.message.inpatient_record) { frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', data.message.inpatient_record); frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', data.message.inpatient_status); + } else { + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', ''); + frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', ''); } } }); } else { + frappe.model.set_value(frm.doctype, frm.docname, 'patient_mame', ''); frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', ''); frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', ''); frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', ''); diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index ff7ecc926f3..f1831ad7a16 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -17,17 +17,16 @@ "patient_name", "patient_sex", "patient_age", + "inpatient_record", + "inpatient_status", "column_break_6", + "company", + "encounter_date", + "encounter_time", "practitioner", "practitioner_name", "medical_department", - "encounter_date", - "encounter_time", "invoiced", - "section_break_1", - "inpatient_record", - "column_break_17", - "inpatient_status", "sb_symptoms", "symptoms", "symptoms_in_print", @@ -48,7 +47,6 @@ "section_break_33", "encounter_comment", "sb_refs", - "company", "amended_from" ], "fields": [ @@ -59,12 +57,6 @@ "options": "Inpatient Record", "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "section_break_1", - "fieldtype": "Section Break", - "label": "Inpatient Details" - }, { "fieldname": "naming_series", "fieldtype": "Select", @@ -79,10 +71,10 @@ "ignore_user_permissions": 1, "label": "Appointment", "options": "Patient Appointment", - "search_index": 1 + "search_index": 1, + "set_only_once": 1 }, { - "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -93,7 +85,6 @@ "search_index": 1 }, { - "fetch_from": "patient.patient_name", "fieldname": "patient_name", "fieldtype": "Data", "label": "Patient Name", @@ -206,29 +197,29 @@ { "fieldname": "codification_table", "fieldtype": "Table", - "label": "Medical Coding", + "label": "Medical Codes", "options": "Codification Table" }, { "fieldname": "sb_drug_prescription", "fieldtype": "Section Break", - "label": "Medication" + "label": "Medications" }, { "fieldname": "drug_prescription", "fieldtype": "Table", - "label": "Drug Prescription", + "label": "Items", "options": "Drug Prescription" }, { "fieldname": "sb_test_prescription", "fieldtype": "Section Break", - "label": "Investigation" + "label": "Investigations" }, { "fieldname": "lab_test_prescription", "fieldtype": "Table", - "label": "Lab Prescription", + "label": "Lab Tests", "options": "Lab Prescription" }, { @@ -239,7 +230,7 @@ { "fieldname": "procedure_prescription", "fieldtype": "Table", - "label": "Procedure Prescription", + "label": "Clinical Procedures", "no_copy": 1, "options": "Procedure Prescription" }, @@ -309,10 +300,6 @@ "label": "Inpatient Status", "read_only": 1 }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, { "fieldname": "sb_refs", "fieldtype": "Section Break" @@ -328,7 +315,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-23 23:06:16.348846", + "modified": "2020-04-27 18:59:25.713887", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", From 256b77b463f7bba7dece0d65dec4178ec89b9406 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 27 Apr 2020 20:00:27 +0530 Subject: [PATCH 047/410] fix: filter service unit by company --- .../doctype/clinical_procedure/clinical_procedure.js | 3 ++- .../doctype/patient_appointment/patient_appointment.js | 5 +++-- .../doctype/therapy_session/therapy_session.js | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index 2d30e9003bc..7b992a80b7e 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -43,7 +43,8 @@ frappe.ui.form.on('Clinical Procedure', { return { filters: { 'is_group': false, - 'allow_appointments': true + 'allow_appointments': true, + 'company': frm.doc.company } }; }); diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 488abc21075..f7ed31bfeab 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -32,8 +32,9 @@ frappe.ui.form.on('Patient Appointment', { frm.set_query('service_unit', function(){ return { filters: { - 'is_group': 0, - 'allow_appointments': 1 + 'is_group': false, + 'allow_appointments': true, + 'company': frm.doc.company } }; }); diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js index bb675752bbc..10d657af2e0 100644 --- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js +++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.js @@ -9,6 +9,16 @@ frappe.ui.form.on('Therapy Session', { {fieldname: 'counts_completed', columns: 1}, {fieldname: 'assistance_level', columns: 1} ]; + + frm.set_query('service_unit', function() { + return { + filters: { + 'is_group': false, + 'allow_appointments': true, + 'company': frm.doc.company + } + }; + }); }, refresh: function(frm) { From ca6f3ec977e0a1500425e776fb0f37fc8981cff2 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 27 Apr 2020 22:27:13 +0530 Subject: [PATCH 048/410] fix: set customer name on patient update --- erpnext/healthcare/doctype/patient/patient.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index 6349aece36a..ea63ac74922 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -40,6 +40,7 @@ class Patient(Document): if self.territory: customer.territory = self.territory + customer.customer_name = self.patient_name customer.default_price_list = self.default_price_list customer.default_currency = self.default_currency customer.language = self.language From 0f541cb7ab6e8a93b21756c464e6d37dd4699e01 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 27 Apr 2020 23:42:22 +0530 Subject: [PATCH 049/410] feat: set title for appointment, encounter, procedure and vitals --- .../clinical_procedure.json | 15 ++++++- .../clinical_procedure/clinical_procedure.py | 4 ++ .../patient_appointment.json | 22 ++++++++++- .../patient_appointment.py | 6 +++ .../patient_encounter/patient_encounter.js | 32 +++++++-------- .../patient_encounter/patient_encounter.json | 15 ++++++- .../patient_encounter/patient_encounter.py | 7 ++++ .../doctype/vital_signs/vital_signs.json | 39 +++++++++++++------ .../doctype/vital_signs/vital_signs.py | 7 ++++ 9 files changed, 113 insertions(+), 34 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index 7c4b9a3ed3a..eaf8d80ba8c 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -8,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "naming_series", + "title", "appointment", "procedure_template", "column_break_30", @@ -279,11 +280,21 @@ { "fieldname": "column_break_34", "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-24 22:53:13.156901", + "modified": "2020-04-27 21:36:23.796924", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", @@ -320,6 +331,6 @@ "restrict_to_domain": "Healthcare", "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient_name", + "title_field": "title", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py index 56617e52586..168307b7345 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py @@ -16,6 +16,7 @@ from frappe.model.mapper import get_mapped_doc class ClinicalProcedure(Document): def validate(self): self.set_status() + self.set_title() if self.consume_stock: self.set_actual_qty() @@ -50,6 +51,9 @@ class ClinicalProcedure(Document): elif self.docstatus == 2: self.status = 'Cancelled' + def set_title(self): + self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100] + def complete_procedure(self): if self.consume_stock and self.items: stock_entry = make_stock_entry(self) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 2b0bf216a92..b8a400c6b7a 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -10,6 +10,7 @@ "engine": "InnoDB", "field_order": [ "naming_series", + "title", "status", "patient", "patient_name", @@ -26,6 +27,7 @@ "get_prescribed_therapies", "therapy_plan", "practitioner", + "practitioner_name", "department", "section_break_12", "appointment_type", @@ -327,10 +329,26 @@ { "fieldname": "column_break_36", "fieldtype": "Column Break" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "practitioner.practitioner_name", + "fieldname": "practitioner_name", + "fieldtype": "Data", + "label": "Practitioner Name", + "read_only": 1 } ], "links": [], - "modified": "2020-04-25 17:23:49.841975", + "modified": "2020-04-27 21:36:06.404062", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", @@ -378,7 +396,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "title", "track_changes": 1, "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index a0e3de6b48d..c296065d4a2 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -21,6 +21,7 @@ class PatientAppointment(Document): self.set_appointment_datetime() self.validate_customer_created() self.set_status() + self.set_title() def after_insert(self): self.update_prescription_details() @@ -28,6 +29,11 @@ class PatientAppointment(Document): self.update_fee_validity() send_confirmation_msg(self) + def set_title(self): + self.title = _('{0} with {1} on {2}').format(self.patient_name or self.patient, + self.practitioner_name or self.practitioner, + frappe.utils.format_datetime(self.appointment_datetime))[:100] + def set_status(self): today = getdate() appointment_date = getdate(self.appointment_date) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index b2911d421eb..20ee9daedee 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -157,24 +157,25 @@ frappe.ui.form.on('Patient Encounter', { if (data.message.dob) { age = calculate_age(data.message.dob); } - frappe.model.set_value(frm.doctype, frm.docname, 'patient_mame', data.message.patient_mame); - frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', age); - frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', data.message.sex); - if (data.message.inpatient_record) { - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', data.message.inpatient_record); - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', data.message.inpatient_status); - } else { - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', ''); - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', ''); - } + let values = { + 'patient_age': age, + 'patient_name':data.message.patient_name, + 'patient_sex': data.message.sex, + 'inpatient_record': data.message.inpatient_record, + 'inpatient_status': data.message.inpatient_status + }; + frm.set_value(values); } }); } else { - frappe.model.set_value(frm.doctype, frm.docname, 'patient_mame', ''); - frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', ''); - frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', ''); - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', ''); - frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', ''); + let values = { + 'patient_age': '', + 'patient_name':'', + 'patient_sex': '', + 'inpatient_record': '', + 'inpatient_status': '' + }; + frm.set_value(values); } } }); @@ -226,7 +227,6 @@ let create_vital_signs = function (frm) { } frappe.route_options = { 'patient': frm.doc.patient, - 'appointment': frm.doc.appointment, 'encounter': frm.doc.name, 'company': frm.doc.company }; diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index f1831ad7a16..05eec87398b 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -11,6 +11,7 @@ "engine": "InnoDB", "field_order": [ "naming_series", + "title", "appointment", "appointment_type", "patient", @@ -311,11 +312,21 @@ "in_list_view": 1, "label": "Practitioner Name", "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 18:59:25.713887", + "modified": "2020-04-27 21:58:29.789797", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", @@ -342,7 +353,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient_name", + "title_field": "title", "track_changes": 1, "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 767643bc73a..bab3d3e45f9 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -10,6 +10,9 @@ from frappe.utils import cstr from frappe import _ class PatientEncounter(Document): + def validate(self): + self.set_title() + def on_update(self): if self.appointment: frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed') @@ -26,6 +29,10 @@ class PatientEncounter(Document): def on_submit(self): create_therapy_plan(self) + def set_title(self): + self.title = _('{0} with {1}').format(self.patient_name or self.patient, + self.practitioner_name or self.practitioner)[:100] + def create_therapy_plan(encounter): if len(encounter.therapies): doc = frappe.new_doc('Therapy Plan') diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index fdacda6277d..57fc2369e19 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -2,15 +2,22 @@ "actions": [], "allow_copy": 1, "allow_import": 1, + "autoname": "naming_series:", "beta": 1, "creation": "2017-02-02 11:00:24.853005", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", + "title", "patient", "patient_name", + "inpatient_record", + "appointment", + "encounter", "column_break_2", + "company", "signs_date", "signs_time", "sb_vs", @@ -32,11 +39,6 @@ "column_break_14", "nutrition_note", "sb_references", - "inpatient_record", - "appointment", - "encounter", - "column_break_28", - "company", "amended_from" ], "fields": [ @@ -70,7 +72,8 @@ "fieldname": "appointment", "fieldtype": "Link", "in_filter": 1, - "label": "Appointment", + "label": "Patient Appointment", + "no_copy": 1, "options": "Patient Appointment", "print_hide": 1, "read_only": 1 @@ -83,8 +86,7 @@ "no_copy": 1, "options": "Patient Encounter", "print_hide": 1, - "read_only": 1, - "report_hide": 1 + "read_only": 1 }, { "fieldname": "column_break_2", @@ -237,13 +239,26 @@ "fieldtype": "Section Break" }, { - "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HLC-VTS-.YYYY.-", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-03 23:06:29.786184", + "modified": "2020-04-27 23:18:08.278064", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", @@ -283,7 +298,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "patient", + "title_field": "title", "track_changes": 1, "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index 959e8504c47..5c590cff461 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -9,12 +9,19 @@ from frappe.utils import cstr from frappe import _ class VitalSigns(Document): + def validate(self): + self.set_title() + def on_submit(self): insert_vital_signs_to_medical_record(self) def on_cancel(self): delete_vital_signs_from_medical_record(self) + def set_title(self): + self.title = _('{0} on {1} {2}').format(self.patient_name or self.patient, + frappe.utils.format_date(self.signs_date), frappe.utils.format_time(self.signs_time))[:100] + def insert_vital_signs_to_medical_record(doc): subject = set_subject_field(doc) medical_record = frappe.new_doc('Patient Medical Record') From 1696a2a4b957870f0ff187acdcc6021ca0737dc1 Mon Sep 17 00:00:00 2001 From: anoop Date: Mon, 27 Apr 2020 23:44:41 +0530 Subject: [PATCH 050/410] fix: default sms text in settings - corrected field names --- .../doctype/healthcare_settings/healthcare_settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index de086201795..2f0115c36a7 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -240,7 +240,7 @@ "label": "Patient Registration" }, { - "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!", + "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.name}} . Please note this ID for future reference. \nThank You!", "depends_on": "send_registration_msg", "fieldname": "registration_msg", "fieldtype": "Small Text", @@ -254,7 +254,7 @@ "label": "Appointment Confirmation" }, { - "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", + "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} on {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!", "depends_on": "send_appointment_confirmation", "fieldname": "appointment_confirmation_msg", "fieldtype": "Small Text", @@ -276,7 +276,7 @@ "label": "Appointment Reminder" }, { - "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", + "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!\n", "depends_on": "send_appointment_reminder", "fieldname": "appointment_reminder_msg", "fieldtype": "Small Text", From 1b5eea691cf166562efb1de019599da1d365aede Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 15:51:40 +0530 Subject: [PATCH 051/410] feat: order failed import log by creation, handle rollbacks and commits --- .../doctype/tally_migration/tally_migration.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 46382c1e8f5..cbc6019c6b4 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -29,6 +29,11 @@ VOUCHER_CHUNK_SIZE = 500 class TallyMigration(Document): + def validate(self): + failed_import_log = json.loads(self.failed_import_log) + sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"]) + self.failed_import_log = json.dumps(sorted_failed_import_log) + def autoname(self): if not self.name: self.name = "Tally Migration on " + format_datetime(self.creation) @@ -320,9 +325,11 @@ class TallyMigration(Document): self.set_account_defaults() self.is_master_data_imported = 1 + frappe.db.commit() except: self.publish("Import Master Data", _("Process Failed"), -1, 5) + frappe.db.rollback() self.log() finally: @@ -343,7 +350,9 @@ class TallyMigration(Document): processed_voucher = function(voucher) if processed_voucher: vouchers.append(processed_voucher) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher) return vouchers @@ -545,7 +554,9 @@ class TallyMigration(Document): voucher_doc.insert() voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher_doc) if is_last: @@ -576,14 +587,14 @@ class TallyMigration(Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) doc = data.as_dict() - doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } - stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": stripped_doc, + "doc": doc, "exc": traceback.format_exc() }) self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() + frappe.db.commit() + else: data = data or self.status message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) From 640636bbcd6f84c12c5ed0e74a3e257acb731f52 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:01:59 +0530 Subject: [PATCH 052/410] feat: maintain logs of errors successfully fixed --- .../tally_migration/tally_migration.json | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index 005c8a98c6b..417d9437926 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -38,7 +38,9 @@ "is_day_book_data_imported", "import_log_section", "failed_import_log", - "failed_import_preview" + "fixed_errors_log", + "failed_import_preview", + "fixed_error_log_preview" ], "fields": [ { @@ -220,7 +222,6 @@ "fieldname": "failed_import_log", "fieldtype": "Code", "hidden": 1, - "label": "Failed Import Log", "options": "JSON" }, { @@ -238,10 +239,22 @@ "fieldtype": "Link", "label": "Default Round Off Account", "options": "Account" + }, + { + "default": "[]", + "fieldname": "fixed_errors_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "fixed_error_log_preview", + "fieldtype": "HTML", + "label": "Fixed Error Log" } ], "links": [], - "modified": "2020-04-22 16:01:13.718842", + "modified": "2020-04-28 00:29:18.039826", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 2e1b531f4fce10bdaf74218fa7a0d260d67c1d7a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:02:50 +0530 Subject: [PATCH 053/410] feat: handle resolve and unresolved errors restructured and reused components --- .../tally_migration/tally_migration.js | 299 +++++++++++++----- 1 file changed, 217 insertions(+), 82 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d06fe537ad1..9235bab0683 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -35,8 +35,10 @@ frappe.ui.form.on("Tally Migration", { } }); }, + refresh: function (frm) { - frm.trigger("show_import_log"); + frm.trigger("show_logs_preview"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) @@ -63,6 +65,7 @@ frappe.ui.form.on("Tally Migration", { } } }, + add_button: function (frm, label, method) { frm.add_custom_button( label, @@ -76,94 +79,30 @@ frappe.ui.form.on("Tally Migration", { } ); }, - show_import_log(frm) { - let index = 0; - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - let logs = import_log.slice(0, 20); - let hidden_logs = import_log.slice(20); - frm.toggle_display("import_log_section", logs.length > 0); - - - const getError = (traceback) => { - let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 - let error_line = traceback.substr(exc_error_idx) - let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; - - return error_line.slice(split_str_idx).trim(); + render_html_table(frm, shown_logs, hidden_logs, field) { + if (shown_logs && shown_logs.length > 0) { + frm.toggle_display(field, true); + } else { + frm.toggle_display(field, false); + return } + let rows = get_html_rows(shown_logs, field); + let rows_head; - const cleanDoc = (obj) => { - let temp = obj; - $.each(temp, function(key, value){ - if (value === "" || value === null){ - delete obj[key]; - } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); - } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); - } - }); - return temp; - }; - - let rows = logs - .map(({ doc, exc }) => { - let id = frappe.dom.get_unique_id(); - let traceback = exc; - - let error_message = getError(traceback); - index++; - - let html = ` - -
-
-
${traceback}
-
-
`; - - let show_doc = ` - -
-
-
${JSON.stringify(cleanDoc(doc), null, 1)}
-
-
`; - - let create_button = ` - ` - - return ` - ${index} - -
${doc.doctype}
- - -
${error_message}
-
${html}
-
${show_doc}
- - -
${create_button}
- - `; - }) - .join(""); - - frm.get_field("failed_import_preview").$wrapper.html(` + if (field === "fixed_error_log_preview") { + rows_head = `${__("Meta Data")} + ${__("Unresolve")}` + } else { + rows_head = `${__("Error Message")} + ${__("Create")}` + } + frm.get_field(field).$wrapper.html(` - - + ${rows_head} ${rows} @@ -171,5 +110,201 @@ frappe.ui.form.on("Tally Migration", {
${__("#")} ${__("DocType")}${__("Error Message")}${__("Create")}
`); + }, + + show_error_summary() { + let summary = import_log.reduce((summary, row) => { + if (row.doc) { + if (summary[row.doc.doctype]) { + summary[row.doc.doctype] += 1; + } else { + summary[row.doc.doctype] = 1; + } + } + return summary + }, {}); + console.table(summary); + }, + + show_logs_preview(frm) { + let empty = "[]"; + let import_log = frm.doc.failed_import_log || empty; + let completed_log = frm.doc.fixed_errors_log || empty; + let render_section = !(import_log === completed_log && import_log === empty); + + frm.toggle_display("import_log_section", render_section); + + if (render_section) { + frm.trigger("show_errored_import_log"); + frm.trigger("show_fixed_errors_log"); + } + }, + + show_errored_import_log(frm) { + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview"); + }, + + show_fixed_errors_log(frm) { + let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let logs = completed_log.slice(0, 20); + let hidden_logs = completed_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); } }); + +const getError = (traceback) => { + /* Extracts the Error Message from the Python Traceback or Solved error */ + let is_multiline = traceback.trim().indexOf("\n") != -1; + let message; + + if (is_multiline) { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + message = error_line.slice(split_str_idx).trim(); + } else { + message = traceback; + } + + return message +} + +const cleanDoc = (obj) => { + /* Strips all null and empty values of your JSON object */ + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; +} + +window.unresolve = (document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_fixed_log = fixed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + + failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(failed_log); + frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); +} + +window.create_new_doc = (doctype, document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_failed_log = failed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(modified_failed_log); + frm.doc.fixed_errors_log = JSON.stringify(fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); + frappe.new_doc(doctype, document); +} + +const get_html_rows = (logs, field) => { + let index = 0; + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let show_traceback = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + let mark_as_unresolved = ` + ` + + if (field === "fixed_error_log_preview") { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${mark_as_unresolved}
+ + `; + } else { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${create_button}
+ + `; + } + }) + .join(""); + + return rows +} \ No newline at end of file From 27b7172fa4b888feaf47cf2875405079e83196ef Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:32:15 +0530 Subject: [PATCH 054/410] fix: run summary logger style: indent fixes --- .../doctype/tally_migration/tally_migration.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9235bab0683..bf8ae8c1c6c 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -133,7 +133,7 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - + frm.trigger("show_error_summary"); if (render_section) { frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); @@ -302,9 +302,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }) - .join(""); + } + }).join(""); return rows } \ No newline at end of file From 0d61b35b0bc26ce509801b747bdc2d1d349b7d02 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 04:01:24 +0530 Subject: [PATCH 055/410] fix: table cleanups, updated summary block --- .../tally_migration/tally_migration.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index bf8ae8c1c6c..9cc3c98a569 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -88,32 +88,38 @@ frappe.ui.form.on("Tally Migration", { return } let rows = get_html_rows(shown_logs, field); - let rows_head; + let rows_head, table_caption; + + let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` + And ${hidden_logs.length} more others + `: ""; if (field === "fixed_error_log_preview") { rows_head = `${__("Meta Data")} ${__("Unresolve")}` + table_caption = "Resolved Issues" } else { rows_head = `${__("Error Message")} ${__("Create")}` + table_caption = "Error Log" } + frm.get_field(field).$wrapper.html(` + ${rows_head} ${rows} - - - + ${table_footer}
${table_caption}
${__("#")} ${__("DocType")}
And ${hidden_logs.length} more others
`); }, - show_error_summary() { - let summary = import_log.reduce((summary, row) => { + show_error_summary(frm) { + let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -280,7 +286,6 @@ const get_html_rows = (logs, field) => {
${error_message}
-
${show_traceback}
${show_doc}
@@ -302,8 +307,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }).join(""); + } + }).join(""); return rows } \ No newline at end of file From 6b850b7e71e30c2513ae8f50152eb67788604eb0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 10:25:18 +0530 Subject: [PATCH 056/410] perf: reduce JS memory heap and maintain namespaces --- .../tally_migration/tally_migration.js | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9cc3c98a569..0cb4995ac95 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,6 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.tally_migration"); + frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; @@ -38,10 +40,13 @@ frappe.ui.form.on("Tally Migration", { refresh: function (frm) { frm.trigger("show_logs_preview"); + erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log); + erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log); ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -53,6 +58,7 @@ frappe.ui.form.on("Tally Migration", { } } } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.is_day_book_data_processed) { if (frm.doc.status != "Importing Day Book Data") { @@ -87,7 +93,7 @@ frappe.ui.form.on("Tally Migration", { frm.toggle_display(field, false); return } - let rows = get_html_rows(shown_logs, field); + let rows = erpnext.tally_migration.get_html_rows(shown_logs, field); let rows_head, table_caption; let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` @@ -119,7 +125,7 @@ frappe.ui.form.on("Tally Migration", { }, show_error_summary(frm) { - let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { + let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -147,8 +153,7 @@ frappe.ui.form.on("Tally Migration", { }, show_errored_import_log(frm) { - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - + let import_log = erpnext.tally_migration.failed_import_log; let logs = import_log.slice(0, 20); let hidden_logs = import_log.slice(20); @@ -156,7 +161,7 @@ frappe.ui.form.on("Tally Migration", { }, show_fixed_errors_log(frm) { - let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let completed_log = erpnext.tally_migration.fixed_errors_log; let logs = completed_log.slice(0, 20); let hidden_logs = completed_log.slice(20); @@ -164,7 +169,7 @@ frappe.ui.form.on("Tally Migration", { } }); -const getError = (traceback) => { +erpnext.tally_migration.getError = (traceback) => { /* Extracts the Error Message from the Python Traceback or Solved error */ let is_multiline = traceback.trim().indexOf("\n") != -1; let message; @@ -181,28 +186,28 @@ const getError = (traceback) => { return message } -const cleanDoc = (obj) => { +erpnext.tally_migration.cleanDoc = (obj) => { /* Strips all null and empty values of your JSON object */ let temp = obj; $.each(temp, function(key, value){ if (value === "" || value === null){ delete obj[key]; } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); + erpnext.tally_migration.cleanDoc(value); } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); + $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); } }); return temp; } -window.unresolve = (document) => { +erpnext.tally_migration.unresolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_fixed_log = fixed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -217,13 +222,13 @@ window.unresolve = (document) => { frm.save(); } -window.create_new_doc = (doctype, document) => { +erpnext.tally_migration.resolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_failed_log = failed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -235,17 +240,21 @@ window.create_new_doc = (doctype, document) => { // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); +} + +erpnext.tally_migration.create_new_doc = (doctype, document) => { + erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } -const get_html_rows = (logs, field) => { +erpnext.tally_migration.get_html_rows = (logs, field) => { let index = 0; let rows = logs .map(({ doc, exc }) => { let id = frappe.dom.get_unique_id(); let traceback = exc; - let error_message = getError(traceback); + let error_message = erpnext.tally_migration.getError(traceback); index++; let show_traceback = ` @@ -264,17 +273,17 @@ const get_html_rows = (logs, field) => {
-
${JSON.stringify(cleanDoc(doc), null, 1)}
+
${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}
`; let create_button = ` - ` let mark_as_unresolved = ` - ` From f4926a8205184e21641a1770b9096a8804f2cd64 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 11:21:16 +0530 Subject: [PATCH 057/410] fix: toggle read only for accounts post daybook data processing style: code comments and updates --- .../doctype/tally_migration/tally_migration.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 0cb4995ac95..e94cca54c52 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -45,6 +45,7 @@ frappe.ui.form.on("Tally Migration", { ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1) }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { @@ -145,8 +146,8 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - frm.trigger("show_error_summary"); if (render_section) { + frm.trigger("show_error_summary"); frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); } @@ -202,6 +203,7 @@ erpnext.tally_migration.cleanDoc = (obj) => { } erpnext.tally_migration.unresolve = (document) => { + /* Mark document migration as unresolved ie. move to failed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -217,12 +219,12 @@ erpnext.tally_migration.unresolve = (document) => { frm.doc.failed_import_log = JSON.stringify(failed_log); frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.resolve = (document) => { + /* Mark document migration as resolved ie. move to fixed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -237,12 +239,12 @@ erpnext.tally_migration.resolve = (document) => { frm.doc.failed_import_log = JSON.stringify(modified_failed_log); frm.doc.fixed_errors_log = JSON.stringify(fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.create_new_doc = (doctype, document) => { + /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } From 10d9974e7721257fa1b1b87a6cb0506db9b01c0b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 30 Apr 2020 16:28:52 +0530 Subject: [PATCH 058/410] chore: add total row in sales analytics report --- .../sales_analytics/sales_analytics.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index 71932610a6a..cbc59992483 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,31 +1,31 @@ { - "add_total_row": 0, - "creation": "2018-09-21 12:46:29.451048", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-05-24 05:37:02.866139", - "modified_by": "Administrator", - "module": "Selling", - "name": "Sales Analytics", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Sales Order", - "report_name": "Sales Analytics", - "report_type": "Script Report", + "add_total_row": 1, + "creation": "2018-09-21 12:46:29.451048", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-04-30 16:27:53.112907", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Sales Analytics", + "report_type": "Script Report", "roles": [ { "role": "Stock User" - }, + }, { "role": "Maintenance User" - }, + }, { "role": "Accounts User" - }, + }, { "role": "Sales Manager" } From dfa60e0ed8f428a4bd789a6055b645eab7c57ff8 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 30 Apr 2020 20:06:30 +0530 Subject: [PATCH 059/410] chore: calculate total row month-wise in sales analytics --- .../report/sales_analytics/sales_analytics.json | 4 ++-- .../report/sales_analytics/sales_analytics.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index cbc59992483..bf9edd6cd49 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,5 +1,5 @@ { - "add_total_row": 1, + "add_total_row": 0, "creation": "2018-09-21 12:46:29.451048", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-30 16:27:53.112907", + "modified": "2020-04-30 19:49:02.303320", "modified_by": "Administrator", "module": "Selling", "name": "Sales Analytics", diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index f1726ab8bf7..3fc4633ae3d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -194,6 +194,9 @@ class Analytics(object): def get_rows(self): self.data = [] self.get_periodic_data() + total_row = { + "entity": "Total", + } for entity, period_data in iteritems(self.entity_periodic_data): row = { @@ -207,6 +210,9 @@ class Analytics(object): row[scrub(period)] = amount total += amount + if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 + total_row[scrub(period)] += amount + row["total"] = total if self.filters.tree_type == "Item": @@ -214,9 +220,14 @@ class Analytics(object): self.data.append(row) + self.data.append(total_row) + def get_rows_by_group(self): self.get_periodic_data() out = [] + total_row = { + "entity": "Total", + } for d in reversed(self.group_entries): row = { @@ -232,8 +243,14 @@ class Analytics(object): self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.parent][period] += amount total += amount + + if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 + total_row[scrub(period)] += amount + row["total"] = total out = [row] + out + + out.append(total_row) self.data = out def get_periodic_data(self): From 51305a028be5f7a766dd099f3d93fbea0c28d93d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 30 Apr 2020 04:07:14 +0530 Subject: [PATCH 060/410] feat: prompt that company and COA will be overwritten --- .../doctype/tally_migration/tally_migration.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index e94cca54c52..d9811b58dde 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -73,6 +73,16 @@ frappe.ui.form.on("Tally Migration", { } }, + erpnext_company: function (frm) { + frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => { + if (exists) { + frappe.msgprint( + __("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]), + ); + } + }); + }, + add_button: function (frm, label, method) { frm.add_custom_button( label, From af612ddb6de0c59381d36f1a1212188401990f05 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 1 May 2020 14:39:11 +0530 Subject: [PATCH 061/410] feat: maintain tally voucher numbers feat: create seperate customer and supplier entries for same party if case exists --- .../tally_migration/tally_migration.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index cbc6019c6b4..393c5d4b02b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -145,14 +145,18 @@ class TallyMigration(Document): def remove_parties(parents, children, group_set): customers, suppliers = set(), set() for account in parents: + found = False if self.tally_creditors_account in parents[account]: - children.pop(account, None) + found = True if account not in group_set: suppliers.add(account) - elif self.tally_debtors_account in parents[account]: - children.pop(account, None) + if self.tally_debtors_account in parents[account]: + found = True if account not in group_set: customers.add(account) + if found: + children.pop(account, None) + return children, customers, suppliers def traverse(tree, children, accounts, roots, group_set): @@ -170,6 +174,7 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None + links = [] if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ @@ -180,7 +185,9 @@ class TallyMigration(Document): "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string.strip() in suppliers: + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + + if account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, @@ -189,6 +196,8 @@ class TallyMigration(Document): "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + if party_type: address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ @@ -202,7 +211,7 @@ class TallyMigration(Document): "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, - "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + "links": links }) return parties, addresses @@ -378,6 +387,7 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, @@ -406,6 +416,7 @@ class TallyMigration(Document): "doctype": doctype, party_field: voucher.PARTYNAME.string.strip(), "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), @@ -497,14 +508,21 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): - df = { + tally_guid_df = { "fieldtype": "Data", "fieldname": "tally_guid", "read_only": 1, "label": "Tally GUID" } - for doctype in doctypes: - create_custom_field(doctype, df) + tally_voucher_no_df = { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number" + } + for df in [tally_guid_df, tally_voucher_no_df]: + for doctype in doctypes: + create_custom_field(doctype, df) def create_price_list(): frappe.get_doc({ From 0c353a64a44fb5567e1de315ca0fef0092c0aa68 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 2 May 2020 18:58:33 +0530 Subject: [PATCH 062/410] fix: un-using buggy JS frappe.new_doc --- .../tally_migration/tally_migration.js | 20 ++++++++++++++++--- .../tally_migration/tally_migration.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d9811b58dde..fd16d1e84aa 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -253,10 +253,24 @@ erpnext.tally_migration.resolve = (document) => { frm.save(); } -erpnext.tally_migration.create_new_doc = (doctype, document) => { +erpnext.tally_migration.create_new_doc = (document) => { /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); - frappe.new_doc(doctype, document); + return frappe.call({ + type: "POST", + method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + args: { + document + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }); } erpnext.tally_migration.get_html_rows = (logs, field) => { @@ -290,7 +304,7 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { `; let create_button = ` - ` diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 393c5d4b02b..d9c5852a6ed 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -28,6 +28,16 @@ PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 +@frappe.whitelist() +def new_doc(document): + document = json.loads(document) + doctype = document.pop("doctype") + document.pop("name", None) + doc = frappe.new_doc(doctype) + doc.update(document) + + return doc + class TallyMigration(Document): def validate(self): failed_import_log = json.loads(self.failed_import_log) From 971170c59025cfebd38c7bb7a2bd9633064625d8 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 2 May 2020 19:39:03 +0530 Subject: [PATCH 063/410] fix: incorrect total in sales analytics for customer/item group --- erpnext/selling/report/sales_analytics/sales_analytics.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 3fc4633ae3d..c0c11339ca8 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -225,9 +225,6 @@ class Analytics(object): def get_rows_by_group(self): self.get_periodic_data() out = [] - total_row = { - "entity": "Total", - } for d in reversed(self.group_entries): row = { @@ -245,12 +242,10 @@ class Analytics(object): total += amount if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - total_row[scrub(period)] += amount row["total"] = total out = [row] + out - out.append(total_row) self.data = out def get_periodic_data(self): From 826b39559db854df6a6c22447b7aacdb379055bc Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sat, 2 May 2020 19:50:45 +0530 Subject: [PATCH 064/410] fix: review fixes --- erpnext/selling/report/sales_analytics/sales_analytics.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index c0c11339ca8..97d9322918d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -241,8 +241,6 @@ class Analytics(object): self.entity_periodic_data[d.parent][period] += amount total += amount - if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - row["total"] = total out = [row] + out From ab9bb08c4b100fc4abf2361968c9706cb03781da Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Sun, 3 May 2020 14:53:13 +0530 Subject: [PATCH 065/410] fix: test --- .../report/sales_analytics/test_analytics.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 4d81a1e4dda..0a5198ccc53 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -83,6 +83,22 @@ class TestAnalytics(unittest.TestCase): "feb_2018": 0.0, "mar_2018": 0.0, "total": 3000.0 + }, + { + "entity": "Total", + "entity_name": "", + "apr_2017": 0.0, + "may_2017": 0.0, + "jun_2017": 2000.0, + "jul_2017": 1000.0, + "aug_2017": 0.0, + "sep_2017": 1500.0, + "oct_2017": 1000.0, + "nov_2017": 0.0, + "dec_2017": 0.0, + "jan_2018": 0.0, + "feb_2018": 0.0, + "mar_2018": 0.0 } ] result = sorted(report[1], key=lambda k: k['entity']) From 100453c64fdf9bb8d480ea37242b421499bd656e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 19:32:20 +0530 Subject: [PATCH 066/410] feat: added open count tag --- erpnext/projects/desk_page/projects/projects.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index a07cdffcbeb..52e502bbed6 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -28,7 +28,7 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-04-01 11:28:51.245756", + "modified": "2020-05-04 18:08:54.363929", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -37,6 +37,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#4d4da8", "format": "{} Assigned", "label": "Task", "link_to": "Task", @@ -44,8 +45,11 @@ "type": "DocType" }, { + "color": "#4d4da8", + "format": "{} Open", "label": "Project", "link_to": "Project", + "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, { From 52cc6ad456940798be40923e216898e1b3a6082e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 4 May 2020 20:24:00 +0530 Subject: [PATCH 067/410] fix: test --- .../report/sales_analytics/test_analytics.py | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 0a5198ccc53..7e8501dcc15 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -33,6 +33,21 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ + { + 'entity': 'Total', + 'apr_2017': 0.0, + 'may_2017': 0.0, + 'jun_2017': 2000.0, + 'jul_2017': 1000.0, + 'aug_2017': 0.0, + 'sep_2017': 1500.0, + 'oct_2017': 1000.0, + 'nov_2017': 0.0, + 'dec_2017': 0.0, + 'jan_2018': 0.0, + 'feb_2018': 2000.0, + 'mar_2018': 0.0 + }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", @@ -83,22 +98,6 @@ class TestAnalytics(unittest.TestCase): "feb_2018": 0.0, "mar_2018": 0.0, "total": 3000.0 - }, - { - "entity": "Total", - "entity_name": "", - "apr_2017": 0.0, - "may_2017": 0.0, - "jun_2017": 2000.0, - "jul_2017": 1000.0, - "aug_2017": 0.0, - "sep_2017": 1500.0, - "oct_2017": 1000.0, - "nov_2017": 0.0, - "dec_2017": 0.0, - "jan_2018": 0.0, - "feb_2018": 0.0, - "mar_2018": 0.0 } ] result = sorted(report[1], key=lambda k: k['entity']) @@ -150,6 +149,21 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ + { + 'entity': 'Total', + 'apr_2017': 0.0, + 'may_2017': 0.0, + 'jun_2017': 20.0, + 'jul_2017': 10.0, + 'aug_2017': 0.0, + 'sep_2017': 15.0, + 'oct_2017': 10.0, + 'nov_2017': 0.0, + 'dec_2017': 0.0, + 'jan_2018': 0.0, + 'feb_2018': 20.0, + 'mar_2018': 0.0 + }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", From 1607891a3c3bbbbad10c2b6c4509a166df547301 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:16:56 +0530 Subject: [PATCH 068/410] feat: added basic project summary report --- .../report/project_summary/__init__.py | 0 .../report/project_summary/project_summary.js | 23 +++ .../project_summary/project_summary.json | 27 ++++ .../report/project_summary/project_summary.py | 142 ++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 erpnext/projects/report/project_summary/__init__.py create mode 100644 erpnext/projects/report/project_summary/project_summary.js create mode 100644 erpnext/projects/report/project_summary/project_summary.json create mode 100644 erpnext/projects/report/project_summary/project_summary.py diff --git a/erpnext/projects/report/project_summary/__init__.py b/erpnext/projects/report/project_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js new file mode 100644 index 00000000000..15367acd7d3 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.js @@ -0,0 +1,23 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Project Summary"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "Select", + "options": "Open\nComplete\nCancelled", + "default": "Open" + } + ] +}; diff --git a/erpnext/projects/report/project_summary/project_summary.json b/erpnext/projects/report/project_summary/project_summary.json new file mode 100644 index 00000000000..0b18b3e2784 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-05-04 19:31:54.575765", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-04 19:32:53.177213", + "modified_by": "Administrator", + "module": "Projects", + "name": "Project Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Project", + "report_name": "Project Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Projects User" + }, + { + "role": "Projects Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py new file mode 100644 index 00000000000..c11c4ca9ff1 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -0,0 +1,142 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns = get_columns() + data = [] + + data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date"], order_by="expected_end_date") + + for project in data: + project["total_tasks"] = frappe.db.count("Task", filters={"project": project.name}) + project["completed_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Completed"}) + project["overdue_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Overdue"}) + + chart = get_chart_data(data) + report_summary = get_report_summary(data) + + return columns, data, None, chart, report_summary + +def get_columns(): + return [ + { + "fieldname": "name", + "label": _("Project"), + "fieldtype": "Link", + "options": "Project", + "width": 200 + }, + { + "fieldname": "status", + "label": _("Status"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "total_tasks", + "label": _("Total Tasks"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "completed_tasks", + "label": _("Tasks Completed"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "overdue_tasks", + "label": _("Tasks Overdue"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "percent_complete", + "label": _("Completion"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "expected_start_date", + "label": _("Start Date"), + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "expected_end_date", + "label": _("End Date"), + "fieldtype": "Date", + "width": 120 + }, + ] + +def get_chart_data(data): + labels = [] + total = [] + completed = [] + overdue = [] + + for project in data: + labels.append(project.name) + total.append(project.total_tasks) + completed.append(project.completed_tasks) + overdue.append(project.overdue_tasks) + + return { + "data": { + 'labels': labels, + 'datasets': [ + { + "name": "Total Tasks", + "values": total + }, + { + "name": "Tasks Completed", + "values": completed + }, + { + "name": "Tasks Overdue", + "values": overdue + } + ] + }, + "type": "bar", + "colors": ["#7679fc", "#98d85b", "#fc4f51"] + } + +def get_report_summary(data): + avg_completion = sum([project.percent_complete for project in data]) / len(data) + total = sum([project.total_tasks for project in data]) + total_overdue = sum([project.overdue_tasks for project in data]) + completed = sum([project.completed_tasks for project in data]) + + return [ + { + "value": avg_completion, + "indicator": "Green" if avg_completion > 50 else "Red", + "label": "Average Completion", + "datatype": "Percent", + }, + { + "value": total, + "indicator": "Blue", + "label": "Total Tasks", + "datatype": "Int", + }, + { + "value": completed, + "indicator": "Green", + "label": "Completed Tasks", + "datatype": "Int", + }, + { + "value": total_overdue, + "indicator": "Green" if total_overdue == 0 else "Red", + "label": "Overdue Tasks", + "datatype": "Int", + } + ] From 813bc498689bf53cc7dcb7f9e8bed8737caec730 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:27:26 +0530 Subject: [PATCH 069/410] feat: added Report Summary Chart as fixture --- .../setup_wizard/data/dashboard_charts.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index bb8c1319bf3..ebe183e4c5e 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -31,7 +31,14 @@ def get_default_dashboards(): { "chart": "Income" }, { "chart": "Expenses" } ] - } + }, + { + "doctype": "Dashboard", + "dashboard_name": "Project", + "charts": [ + { "chart": "Report Summary", "width": "Full" } + ] + }, ], "Charts": [ { @@ -107,7 +114,18 @@ def get_default_dashboards(): "document_type": "Sales Invoice", "type": "Bar", "width": "Half" - } + }, + { + 'doctype': 'Dashboard Chart', + 'name': 'Project Summary', + 'chart_name': 'Project Summary', + 'chart_type': 'Report', + 'report_name': 'Project Summary', + 'is_public': 1, + 'filters_json': json.dumps({"company": company.name, "status": "Open"}), + 'type': 'Bar', + 'custom_options': '{"type": "bar", "colors": ["#7679fc", "#98d85b", "#fc4f51"], "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}', + }, ] } From c7f5724fad8259d381b91f62c47fd96f0c70c8ff Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:28:24 +0530 Subject: [PATCH 070/410] feat: added project summary chart to desktop --- erpnext/projects/desk_page/projects/projects.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 52e502bbed6..4d4450dbf53 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -17,18 +17,22 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Project Summary", + "label": "Open Projects" + } + ], "creation": "2020-03-02 15:46:04.874669", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-04 18:08:54.363929", + "modified": "2020-05-04 20:27:51.591365", "modified_by": "Administrator", "module": "Projects", "name": "Projects", From 4ea122662a59952b75144b182115c44c597777be Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:29:51 +0530 Subject: [PATCH 071/410] refactor: less verbose labels for charts --- erpnext/projects/report/project_summary/project_summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index c11c4ca9ff1..81e63cf70da 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -95,11 +95,11 @@ def get_chart_data(data): "values": total }, { - "name": "Tasks Completed", + "name": "Completed", "values": completed }, { - "name": "Tasks Overdue", + "name": "Overdue", "values": overdue } ] From 39fe68880941e79cc8bbf50a717df7326ee3aeea Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 13:05:38 +0530 Subject: [PATCH 072/410] fix: dashboard chart link --- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index ebe183e4c5e..7edd5e3d4ac 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -36,7 +36,7 @@ def get_default_dashboards(): "doctype": "Dashboard", "dashboard_name": "Project", "charts": [ - { "chart": "Report Summary", "width": "Full" } + { "chart": "Project Summary", "width": "Full" } ] }, ], From cc7488a90d579da62b17d8f404d967633bfe36ef Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 13:05:53 +0530 Subject: [PATCH 073/410] feat: rerun patch to add default dashboards --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 52953996956..1f8fabd2990 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -630,7 +630,7 @@ execute:frappe.reload_doc('desk', 'doctype', 'dashboard') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field') -erpnext.patches.v12_0.add_default_dashboards +erpnext.patches.v12_0.add_default_dashboards # 2020-05-05 erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Report", "Loan Repayment") From d097eaef6304a247f7f7681e79acd38bc5ce3349 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 5 May 2020 15:57:49 +0530 Subject: [PATCH 074/410] Appending Email and Phone in Child Table --- erpnext/selling/doctype/customer/customer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 50e719f02e8..d0db6d62a04 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -164,6 +164,8 @@ class Customer(TransactionBase): contact.phone = lead.phone contact.mobile_no = lead.mobile_no contact.is_primary_contact = 1 + contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) + contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.append('links', dict(link_doctype='Customer', link_name=self.name)) contact.flags.ignore_permissions = self.flags.ignore_permissions contact.autoname() From 6e95d248e35abeebeae65c65fa788afa1e1bc925 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 5 May 2020 16:15:07 +0530 Subject: [PATCH 075/410] fix: work order operation completed qty --- erpnext/manufacturing/doctype/job_card/job_card.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e9627a55145..e43b98aee1a 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -206,30 +206,31 @@ class JobCard(Document): for_quantity, time_in_mins = 0, 0 from_time_list, to_time_list = [], [] - + field = "operation_id" if self.operation_id else "operation" data = frappe.get_all('Job Card', fields = ["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"], filters = {"docstatus": 1, "work_order": self.work_order, - "workstation": self.workstation, "operation": self.operation}) + "workstation": self.workstation, field: self.get(field)}) if data and len(data) > 0: for_quantity = data[0].completed_qty time_in_mins = data[0].time_in_mins - if for_quantity: + if self.get(field): time_data = frappe.db.sql(""" SELECT min(from_time) as start_time, max(to_time) as end_time FROM `tabJob Card` jc, `tabJob Card Time Log` jctl WHERE jctl.parent = jc.name and jc.work_order = %s - and jc.workstation = %s and jc.operation = %s and jc.docstatus = 1 - """, (self.work_order, self.workstation, self.operation), as_dict=1) + and jc.workstation = %s and jc.{0} = %s and jc.docstatus = 1 + """.format(field), (self.work_order, self.workstation, self.get(field)), as_dict=1) wo = frappe.get_doc('Work Order', self.work_order) + work_order_field = "name" if field == "operation_id" else field for data in wo.operations: - if data.workstation == self.workstation and data.operation == self.operation: + if data.get(work_order_field) == self.get(field) and data.workstation == self.workstation: data.completed_qty = for_quantity data.actual_operation_time = time_in_mins data.actual_start_time = time_data[0].start_time if time_data else None From 5f1240bcc66d1873dc4e84c09df2e449f4033627 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:44:02 +0530 Subject: [PATCH 076/410] feat: updated charts to use stacked layout --- .../projects/report/project_summary/project_summary.py | 10 +++++----- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 81e63cf70da..7f534bf5245 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -90,10 +90,6 @@ def get_chart_data(data): "data": { 'labels': labels, 'datasets': [ - { - "name": "Total Tasks", - "values": total - }, { "name": "Completed", "values": completed @@ -101,7 +97,11 @@ def get_chart_data(data): { "name": "Overdue", "values": overdue - } + }, + { + "name": "Total Tasks", + "values": total + }, ] }, "type": "bar", diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 7edd5e3d4ac..ccb23c07e85 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -124,7 +124,7 @@ def get_default_dashboards(): 'is_public': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', - 'custom_options': '{"type": "bar", "colors": ["#7679fc", "#98d85b", "#fc4f51"], "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}', + 'custom_options': '{"type": "bar", "colors": ["#98d85b", "#fc4f51", "#7679fc"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', }, ] } From 945aa08d92185549265c01a6045fba6e15053e8b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:46:21 +0530 Subject: [PATCH 077/410] feat: show stacked in report --- erpnext/projects/report/project_summary/project_summary.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 7f534bf5245..dc641766704 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -105,7 +105,10 @@ def get_chart_data(data): ] }, "type": "bar", - "colors": ["#7679fc", "#98d85b", "#fc4f51"] + "colors": ["#98d85b", "#fc4f51", "#7679fc"], + "barOptions": { + "stacked": True + } } def get_report_summary(data): From 81f8fbb042eb3554e540a6f25053fb47f8eb052d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:53:29 +0530 Subject: [PATCH 078/410] feat: update colors --- .../projects/report/project_summary/project_summary.py | 10 +++++----- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index dc641766704..66d68bda06a 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -90,14 +90,14 @@ def get_chart_data(data): "data": { 'labels': labels, 'datasets': [ - { - "name": "Completed", - "values": completed - }, { "name": "Overdue", "values": overdue }, + { + "name": "Completed", + "values": completed + }, { "name": "Total Tasks", "values": total @@ -105,7 +105,7 @@ def get_chart_data(data): ] }, "type": "bar", - "colors": ["#98d85b", "#fc4f51", "#7679fc"], + "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "barOptions": { "stacked": True } diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index d54462e1be4..9ce64eb9d92 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -125,7 +125,7 @@ def get_default_dashboards(): 'is_public': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', - 'custom_options': '{"type": "bar", "colors": ["#98d85b", "#fc4f51", "#7679fc"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', + 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', }, { "doctype": "Dashboard Chart", From cc989b62bd6e881b797cf3f3eebd236cdcb07284 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 5 May 2020 23:58:08 +0530 Subject: [PATCH 079/410] feat: Buying Desk, Dashboard and Reports --- erpnext/buying/desk_page/buying/buying.json | 79 +++-- .../purchase_order_analysis/__init__.py | 0 .../purchase_order_analysis.js | 86 ++++++ .../purchase_order_analysis.json | 33 +++ .../purchase_order_analysis.py | 277 ++++++++++++++++++ .../requested_items_to_order/__init__.py | 0 .../requested_items_to_order.js | 64 ++++ .../requested_items_to_order.json | 34 +++ .../requested_items_to_order.py | 211 +++++++++++++ 9 files changed, 753 insertions(+), 31 deletions(-) create mode 100644 erpnext/buying/report/purchase_order_analysis/__init__.py create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py create mode 100644 erpnext/buying/report/requested_items_to_order/__init__.py create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.js create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.json create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.py diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 5e764cf8bbe..432f3c557b8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -2,24 +2,24 @@ "cards": [ { "hidden": 0, - "label": "Supplier", - "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + "label": "Buying", + "links": "[ \n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, - "label": "Purchasing", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" + "label": "Items & Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, "label": "Settings", "links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]" }, + { + "hidden": 0, + "label": "Supplier", + "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Supplier Scorecard", @@ -28,32 +28,33 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items to Order\",\n \"name\": \"Requested Items to Order\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" } ], + "cards_label": "Masters & Reports ", "category": "Modules", "charts": [ { - "chart_name": "Expenses", - "label": "Expenses" + "chart_name": "Purchase Analytics", + "label": "Buying Analytics" } ], + "charts_label": "Buying Dashboard", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-04-01 11:28:51.192097", + "modified": "2020-05-05 23:48:25.788598", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -62,32 +63,48 @@ "pin_to_top": 0, "shortcuts": [ { - "format": "{} Unpaid", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Unpaid\"\n}", - "type": "DocType" - }, - { - "format": "{} to receive", + "color": "#ffe8cd", + "format": "{} to Receive", "label": "Purchase Order", "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Receive\"\n}", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { + "color": "#ffe8cd", + "format": "{} Pending", + "label": "Material Request", + "link_to": "Material Request", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} to Bill ", + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} Unpaid / Overdue", + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", + "type": "DocType" + }, + { + "color": "#cef6d1", + "format": "{} Active", "label": "Supplier Quotation", "link_to": "Supplier Quotation", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", "type": "DocType" }, { - "label": "Accounts Payable", - "link_to": "Accounts Payable", - "type": "Report" - }, - { - "label": "Purchase Register", - "link_to": "Purchase Register", + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", "type": "Report" } ] diff --git a/erpnext/buying/report/purchase_order_analysis/__init__.py b/erpnext/buying/report/purchase_order_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js new file mode 100644 index 00000000000..24abb6d44ab --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -0,0 +1,86 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Purchase Order Analysis"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "purchase_order", + "label": __("Purchase Order"), + "fieldtype": "Link", + "width": "80", + "options": "Purchase Order", + "get_query": () =>{ + return { + filters: { "docstatus": 1 } + } + } + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": "80", + get_data: function(txt) { + let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "description": "" + }) + } + return options + } + }, + { + "fieldname": "chart_based_on", + "label": __("Chart Based On"), + "fieldtype": "Select", + "width": "80", + "options": "Quantity\nAmount", + "default": "Quantity" + }, + { + "fieldname": "group_by_po", + "label": __("Group by Purchase Order"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json new file mode 100644 index 00000000000..196aaaed224 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "creation": "2020-05-04 18:41:28.625119", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-04 18:41:28.625119", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Order", + "report_name": "Purchase Order Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + }, + { + "role": "Supplier" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py new file mode 100644 index 00000000000..78b86636e07 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -0,0 +1,277 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [], [] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + data = get_data(conditions, filters) + + if not data: + return [], [] + + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and po.company = '{0}'".format(filters.get("company")) + + if filters.get("purchase_order"): + conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + + if filters.get("status"): + conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + + return conditions + +def get_data(conditions, filters): + status = filters.get("status") + # temporary fix for dashboard chart + if status is None: + status = [] + + data = frappe.db.sql(""" + SELECT + po.transaction_date as date, + poi.schedule_date as required_date, + po.name as purchase_order, + po.status, po.supplier, poi.item_code, + poi.qty, poi.received_qty, + (poi.qty - poi.received_qty) AS pending_qty, + IFNULL(pii.qty, 0) as billed_qty, + poi.base_amount as amount, + (poi.received_qty * poi.base_rate) as received_qty_amount, + (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount, + (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount, + po.set_warehouse as warehouse, + po.company, poi.name + FROM + `tabPurchase Order` po, + `tabPurchase Order Item` poi + LEFT JOIN `tabPurchase Invoice Item` pii + ON pii.po_detail = poi.name + WHERE + poi.parent = po.name + and po.status not in ('Stopped', 'Closed') + and po.docstatus = 1 + {0} + GROUP BY poi.name + ORDER BY po.transaction_date ASC + """.format(conditions), tuple(status), as_dict=1) + + return data + +def prepare_data(data, filters): + completed, pending = 0,0 + chart_based_on = filters.get("chart_based_on") + pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" + completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + + if filters.get("group_by_po"): + purchase_order_map = {} + + for row in data: + # sum data for chart + completed += row[completed_field] + pending += row[pending_field] + + # prepare data for report view + row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) + + if filters.get("group_by_po"): + po_name = row["purchase_order"] + + if not po_name in purchase_order_map: + # create an entry + row_copy = copy.deepcopy(row) + purchase_order_map[po_name] = row_copy + else: + # update existing entry + po_row = purchase_order_map[po_name] + po_row["required_date"] = min(getdate(po_row["required_date"]), getdate(row["required_date"])) + + # sum numeric columns + fields = ["qty", "received_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount", + "received_qty_amount", "billed_amount", "pending_amount"] + for field in fields: + po_row[field] = flt(row[field]) + flt(po_row[field]) + + chart_data = prepare_chart_data(chart_based_on, pending, completed) + + if filters.get("group_by_po"): + data = [] + for po in purchase_order_map: + data.append(purchase_order_map[po]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(chart_based_on, pending, completed): + labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] + + return { + "data" : { + "labels": labels, + "datasets": [ + {"values": [pending, completed]} + ] + }, + "type": 'donut', + "height": 300 + } + +def get_columns(filters): + columns = [ + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 90 + }, + { + "label": _("Purchase Order"), + "fieldname": "purchase_order", + "fieldtype": "Link", + "options": "Purchase Order", + "width": 160 + }, + { + "label":_("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": 130 + }] + + if not filters.get("group_by_po"): + columns.append({ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Received Qty"), + "fieldname": "received_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Pending Qty"), + "fieldname": "pending_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Billed Qty"), + "fieldname": "billed_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Qty to Bill"), + "fieldname": "qty_to_bill", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Received Qty Amount"), + "fieldname": "received_qty_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100 + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns + diff --git a/erpnext/buying/report/requested_items_to_order/__init__.py b/erpnext/buying/report/requested_items_to_order/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js new file mode 100644 index 00000000000..21adb135476 --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js @@ -0,0 +1,64 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Requested Items to Order"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "material_request", + "label": __("Material Request"), + "fieldtype": "Link", + "width": "80", + "options": "Material Request", + "get_query": () =>{ + return { + filters: { + "docstatus": 1, + "material_request_type": "Purchase", + "per_received": ["<", 100] + } + } + } + }, + { + "fieldname": "group_by_mr", + "label": __("Group by Material Request"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "ordered_qty" && data && data.ordered_qty > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json new file mode 100644 index 00000000000..4a0578be4bf --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json @@ -0,0 +1,34 @@ +{ + "add_total_row": 1, + "creation": "2020-05-04 20:23:57.750719", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-05 13:05:51.723951", + "modified_by": "Administrator", + "module": "Buying", + "name": "Requested Items to Order", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Material Request", + "report_name": "Requested Items to Order", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + }, + { + "role": "Purchase User" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py new file mode 100644 index 00000000000..a021d3c1cab --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py @@ -0,0 +1,211 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [],[] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + #get queried data + data = get_data(filters, conditions) + + #prepare data for report and chart views + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = '' + + if filters.get("from_date") and filters.get("to_date"): + conditions += " and mr.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and mr.company = '{0}'".format(filters.get("company")) + + if filters.get("material_request"): + conditions += " and mr.name = '{0}'".format(filters.get("material_request")) + + return conditions + +def get_data(filters, conditions): + data = frappe.db.sql(""" + select + mr.name as material_request, + mr.transaction_date as date, + mr_item.schedule_date as required_date, + mr_item.item_code as item_code, + sum(ifnull(mr_item.stock_qty, 0)) as qty, + ifnull(mr_item.stock_uom, '') as uom, + sum(ifnull(mr_item.ordered_qty, 0)) as ordered_qty, + (sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as qty_to_order, + mr_item.item_name as item_name, + mr.company as company + from + `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where + mr_item.parent = mr.name + and mr.material_request_type = "Purchase" + and mr.docstatus = 1 + and mr.status != "Stopped" + {conditions} + group by mr.name, mr_item.item_code + having + sum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0)) + order by mr.transaction_date, mr.schedule_date""".format(conditions=conditions), as_dict=1) + + return data + +def prepare_data(data, filters): + """Prepare consolidated Report data and Chart data""" + material_request_map = {} + + for row in data: + if not row["material_request"] in material_request_map: + # create an entry with mr as key + row_copy = copy.deepcopy(row) + material_request_map[row["material_request"]] = row_copy + else: + mr_row = material_request_map[row["material_request"]] + mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) + + #sum numeric rows + fields = ["qty", "ordered_qty", "qty_to_order"] + for field in fields: + mr_row[field] = flt(mr_row[field]) + flt(row[field]) + + chart_data = prepare_chart_data(material_request_map) + + if filters.get("group_by_mr"): + data =[] + for mr in material_request_map: + data.append(material_request_map[mr]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(data): + labels, qty_to_order, ordered_qty = [], [], [] + + for row in data: + mr_row = data[row] + labels.append(mr_row["material_request"]) + qty_to_order.append(mr_row["qty_to_order"]) + ordered_qty.append(mr_row["ordered_qty"]) + + chart_data = { + "data" : { + "labels": labels, + "datasets": [ + { + 'name': _('Qty to Order'), + 'values': qty_to_order + }, + { + 'name': _('Ordered Qty'), + 'values': ordered_qty + } + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 + }, + } + + return chart_data + +def get_columns(filters): + columns = [ + { + "label": _("Material Request"), + "fieldname": "material_request", + "fieldtype": "Link", + "options": "Material Request", + "width": 150 + }, + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 100 + } + ] + + if not filters.get("group_by_mr"): + columns.extend([{ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }, + { + "label":_("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("UOM"), + "fieldname": "uom", + "fieldtype": "Data", + "width": 100, + }]) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Ordered Qty"), + "fieldname": "ordered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Qty to Order"), + "fieldname": "qty_to_order", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns From d078883cd436ae878f6b38607f1f3db693f62d04 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Wed, 6 May 2020 01:13:51 +0530 Subject: [PATCH 080/410] fixing callback urls --- .../linkedin_settings/linkedin_settings.js | 2 ++ .../linkedin_settings/linkedin_settings.py | 6 +++--- .../twitter_settings/twitter_settings.js | 2 ++ .../twitter_settings/twitter_settings.py | 19 +++++++++++-------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js index 50b98e9ce12..263005ef6c5 100644 --- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js +++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js @@ -62,6 +62,8 @@ frappe.ui.form.on('LinkedIn Settings', { callback : function(r) { window.location.href = r.message; } + }).fail(function() { + frappe.dom.unfreeze(); }); } }, diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py index 5df35df3ddf..bdde9eed379 100644 --- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py +++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py @@ -15,7 +15,7 @@ class LinkedInSettings(Document): params = urlencode({ "response_type":"code", "client_id": self.consumer_key, - "redirect_uri": get_site_url(frappe.local.site) + "/?cmd=erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback", + "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?", "scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social" }) @@ -30,7 +30,7 @@ class LinkedInSettings(Document): "code": code, "client_id": self.consumer_key, "client_secret": self.get_password(fieldname="consumer_secret"), - "redirect_uri": get_site_url(frappe.local.site) + "/?cmd=erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback", + "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?", } headers = { "Content-Type": "application/x-www-form-urlencoded" @@ -154,7 +154,7 @@ class LinkedInSettings(Document): return response -@frappe.whitelist() +@frappe.whitelist(allow_guest=True) def callback(code=None, error=None, error_description=None): if not error: linkedin_settings = frappe.get_doc("LinkedIn Settings") diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.js b/erpnext/crm/doctype/twitter_settings/twitter_settings.js index b55946a8bd8..f6f431ca5c9 100644 --- a/erpnext/crm/doctype/twitter_settings/twitter_settings.js +++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.js @@ -47,6 +47,8 @@ frappe.ui.form.on('Twitter Settings', { callback : function(r) { window.location.href = r.message; } + }).fail(function() { + frappe.dom.unfreeze(); }); } }, diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py index 64f53b5eb05..7616b4c027e 100644 --- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py +++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py @@ -12,13 +12,12 @@ from tweepy.error import TweepError class TwitterSettings(Document): def get_authorize_url(self): - callback_url = "{0}/?cmd=erpnext.crm.doctype.twitter_settings.twitter_settings.callback".format(frappe.utils.get_url()) + callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url()) auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url) - try: redirect_url = auth.get_authorization_url() return redirect_url - except: + except tweepy.TweepError as e: frappe.msgprint(_("Error! Failed to get request token.")) frappe.throw(_('Invalid {0} or {1}').format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key"))) @@ -91,8 +90,12 @@ class TwitterSettings(Document): frappe.db.commit() frappe.throw(content["message"],title="Twitter Error {0} {1}".format(e.response.status_code, e.response.reason)) -@frappe.whitelist() -def callback(oauth_token, oauth_verifier): - twitter_settings = frappe.get_single("Twitter Settings") - twitter_settings.get_access_token(oauth_token,oauth_verifier) - frappe.db.commit() +@frappe.whitelist(allow_guest=True) +def callback(oauth_token = None, oauth_verifier = None): + if oauth_token and oauth_verifier: + twitter_settings = frappe.get_single("Twitter Settings") + twitter_settings.get_access_token(oauth_token,oauth_verifier) + frappe.db.commit() + else: + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = get_url_to_form("Twitter Settings","Twitter Settings") From b2103f547073b8441cfbaec4861c4fab74a187d2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 15:18:43 +0530 Subject: [PATCH 081/410] feat: remove old fixtures --- .../account_balance_timeline.py | 2 +- erpnext/patches.txt | 1 - .../patches/v12_0/add_default_dashboards.py | 10 -- .../setup_wizard/data/dashboard_charts.py | 133 ------------------ .../operations/install_fixtures.py | 23 --- 5 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 erpnext/patches/v12_0/add_default_dashboards.py delete mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py 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 c3e2f7db124..5decccb4869 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 @@ -6,7 +6,7 @@ import frappe, json from frappe import _ 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.utils.dashboard import cache_source, get_from_date_from_timespan from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending from frappe.utils.nestedset import get_descendants_of diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ba17b67de15..15235f15a4f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -630,7 +630,6 @@ execute:frappe.reload_doc('desk', 'doctype', 'dashboard') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field') -erpnext.patches.v12_0.add_default_dashboards # 2020-04-05 erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Report", "Loan Repayment") diff --git a/erpnext/patches/v12_0/add_default_dashboards.py b/erpnext/patches/v12_0/add_default_dashboards.py deleted file mode 100644 index 2a91e1b9327..00000000000 --- a/erpnext/patches/v12_0/add_default_dashboards.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2019, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards - -def execute(): - frappe.reload_doc("desk", "doctype", "number_card_link") - frappe.reload_doc("healthcare", "doctype", "patient_appointment") - add_dashboards() diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py deleted file mode 100644 index b182dfc103f..00000000000 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ /dev/null @@ -1,133 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -import frappe -import json - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -def get_default_dashboards(): - company = frappe.get_doc("Company", get_company_for_dashboards()) - income_account = company.default_income_account or get_account("Income Account", company.name) - expense_account = company.default_expense_account or get_account("Expense Account", company.name) - bank_account = company.default_bank_account or get_account("Bank", company.name) - - return { - "Dashboards": [ - { - "doctype": "Dashboard", - "dashboard_name": "Accounts", - "charts": [ - { "chart": "Outgoing Bills (Sales Invoice)" }, - { "chart": "Incoming Bills (Purchase Invoice)" }, - { "chart": "Bank Balance" }, - { "chart": "Income" }, - { "chart": "Expenses" }, - { "chart": "Patient Appointments" } - ] - } - ], - "Charts": [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Daily", - "chart_name": "Patient Appointments", - "timespan": "Last Month", - "color": "#77ecca", - "filters_json": json.dumps({}), - "chart_type": "Count", - "timeseries": 1, - "based_on": "appointment_datetime", - "owner": "Administrator", - "document_type": "Patient Appointment", - "type": "Line", - "width": "Half" - } - ] - } - -def get_account(account_type, company): - accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) - if accounts: - return accounts[0].name diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 3be6f448325..8bb0a0529d7 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -485,8 +485,6 @@ def install_defaults(args=None): # bank account same as a CoA entry pass - add_dashboards() - # Now, with fixtures out of the way, onto concrete stuff records = [ @@ -504,27 +502,6 @@ def install_defaults(args=None): make_records(records) -def add_dashboards(): - from erpnext.setup.setup_wizard.data.dashboard_charts import get_company_for_dashboards - - if not get_company_for_dashboards(): - return - - from erpnext.setup.setup_wizard.data.dashboard_charts import get_default_dashboards - from frappe.modules.import_file import import_file_by_path - - dashboard_data = get_default_dashboards() - - # create account balance timeline before creating dashbaord charts - doctype = "dashboard_chart_source" - docname = "account_balance_timeline" - folder = os.path.dirname(frappe.get_module("erpnext.accounts").__file__) - doc_path = os.path.join(folder, doctype, docname, docname) + ".json" - import_file_by_path(doc_path, force=0, for_sync=True) - - make_records(dashboard_data["Charts"]) - make_records(dashboard_data["Dashboards"]) - def get_fy_details(fy_start_date, fy_end_date): start_year = getdate(fy_start_date).year From 28436d2bc104a7b01dd9f3311ed9ff1a93aebc9a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 15:19:10 +0530 Subject: [PATCH 082/410] feat: added dashboard fixtures for accounts --- erpnext/accounts/dashboard_fixtures.py | 119 +++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 erpnext/accounts/dashboard_fixtures.py diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py new file mode 100644 index 00000000000..30eb5e31156 --- /dev/null +++ b/erpnext/accounts/dashboard_fixtures.py @@ -0,0 +1,119 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + }) + +def get_dashboards(): + return [{ + "name": "Accounts", + "dashboard_name": "Accounts", + "charts": [ + { "chart": "Outgoing Bills (Sales Invoice)" }, + { "chart": "Incoming Bills (Purchase Invoice)" }, + { "chart": "Bank Balance" }, + { "chart": "Income" }, + { "chart": "Expenses" } + ] + }] + +def get_charts(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + income_account = company.default_income_account or get_account("Income Account", company.name) + expense_account = company.default_expense_account or get_account("Expense Account", company.name) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return [ + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Income", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Expenses", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Bank Balance", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Incoming Bills (Purchase Invoice)", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Outgoing Bills (Sales Invoice)", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar" + } + ] + + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None \ No newline at end of file From 8cdcb965c562b470dd2f5fe6d781361b8e4bd09d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 15:19:22 +0530 Subject: [PATCH 083/410] feat: added dashboard fixtures for health care --- erpnext/healthcare/dashboard_fixtures.py | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 erpnext/healthcare/dashboard_fixtures.py diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py new file mode 100644 index 00000000000..856222762b6 --- /dev/null +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -0,0 +1,40 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + }) + +def get_dashboards(): + return [{ + "name": "Healthcare", + "dashboard_name": "Healthcare", + "charts": [ + { "chart": "Patient Appointments" } + ] + }] + +def get_charts(): + return [ + { + "doctype": "Dashboard Chart", + "time_interval": "Daily", + "chart_name": "Patient Appointments", + "timespan": "Last Month", + "color": "#77ecca", + "filters_json": json.dumps({}), + "chart_type": "Count", + "timeseries": 1, + "based_on": "appointment_datetime", + "owner": "Administrator", + "document_type": "Patient Appointment", + "type": "Line", + "width": "Half" + } + ] From 631260b632f01b5b0b16e7fd61a7fd1b40bd562f Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:12:26 +0530 Subject: [PATCH 084/410] chore: Added fixtures and Onboarding. --- erpnext/buying/desk_page/buying/buying.json | 6 +- erpnext/buying/onboarding/buying/buying.json | 42 +++++++++ .../buying_settings/buying_settings.json | 16 ++++ .../create_a_supplier/create_a_supplier.json | 16 ++++ .../introduction_to_buying.json | 16 ++++ .../setup_wizard/data/dashboard_charts.py | 93 +++++++++++++++++++ 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 erpnext/buying/onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/buying_settings/buying_settings.json create mode 100644 erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json create mode 100644 erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 432f3c557b8..9749f3c97fc 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -54,10 +54,11 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-05 23:48:25.788598", + "modified": "2020-05-06 18:10:12.760321", "modified_by": "Administrator", "module": "Buying", "name": "Buying", + "onboarding": "Buying", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -107,5 +108,6 @@ "link_to": "Item-wise Purchase Register", "type": "Report" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json new file mode 100644 index 00000000000..c35309964e7 --- /dev/null +++ b/erpnext/buying/onboarding/buying/buying.json @@ -0,0 +1,42 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-06 16:49:17.433261", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Buying Settings" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json new file mode 100644 index 00000000000..7274c0d62d1 --- /dev/null +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-06 15:53:44.667414", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:51:56.862206", + "modified_by": "Administrator", + "name": "Buying Settings", + "owner": "Administrator", + "reference_document": "Buying Settings", + "title": "Configure Buying Settings." +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json new file mode 100644 index 00000000000..14164c33037 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-06 15:46:09.019329", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-06 17:00:41.446942", + "modified_by": "Administrator", + "name": "Create a Supplier", + "owner": "Administrator", + "reference_document": "Supplier", + "title": "Create a Supplier" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json new file mode 100644 index 00000000000..f1a027af369 --- /dev/null +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-06 15:37:09.477765", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:47:47.378049", + "modified_by": "Administrator", + "name": "Introduction to Buying", + "owner": "Administrator", + "title": "Introduction to Buying", + "video_url": "https://youtu.be/efFajTTQBa8" +} \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index b182dfc103f..8d293973906 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -32,6 +32,17 @@ def get_default_dashboards(): { "chart": "Expenses" }, { "chart": "Patient Appointments" } ] + }, + { + "doctype": "Dashboard", + "dashboard_name": "Buying", + "charts": [ + { "chart": "Purchase Analytics" }, + { "chart": "Material Request Purchase Analysis" }, + { "chart": "Purchase Order Analysis" }, + { "chart": "Requested Items to Order" }, + ] + } ], "Charts": [ @@ -123,6 +134,88 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "type": "Line", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Line", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"], + "lineOptions": {"regionFill": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) } ] } From 4a73bc672b45d612fbdd3623479ac80765dfe306 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:34:07 +0530 Subject: [PATCH 085/410] fix: Old buying config update & bar chart on desk - Line chart was inapprpriate as the values were not continuous. --- erpnext/config/buying.py | 2 +- erpnext/setup/setup_wizard/data/dashboard_charts.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 1d4054786e6..16b49a1e578 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -166,7 +166,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Requested Items To Be Ordered", + "name": "Requested Items To Order", "reference_doctype": "Material Request", "onboard": 1, }, diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 8d293973906..9c654a829d5 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -154,10 +154,9 @@ def get_default_dashboards(): "chart_name": "Purchase Analytics", "custom_options": json.dumps({ "x_field": "entity", - "chart_type": "Line", + "chart_type": "Bar", "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"], - "lineOptions": {"regionFill": 1} + "y_fields": ["total"] }) }, { From 41616d4dd8aa3a723046aa0e07fdaf037c46b363 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 20:21:05 +0530 Subject: [PATCH 086/410] feat: add name to fixture --- erpnext/healthcare/dashboard_fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 856222762b6..fc3d62f2d84 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -25,6 +25,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "time_interval": "Daily", + "name": "Patient Appointments", "chart_name": "Patient Appointments", "timespan": "Last Month", "color": "#77ecca", From 20a1d375f3898cf510b0e0bbbdf9e2e3ff754f93 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 20:26:00 +0530 Subject: [PATCH 087/410] feat: add fixture for project dashboards --- erpnext/projects/dashboard_fixtures.py | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 erpnext/projects/dashboard_fixtures.py diff --git a/erpnext/projects/dashboard_fixtures.py b/erpnext/projects/dashboard_fixtures.py new file mode 100644 index 00000000000..63b3893b76a --- /dev/null +++ b/erpnext/projects/dashboard_fixtures.py @@ -0,0 +1,48 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + }) + +def get_dashboards(): + return [{ + "doctype": "Dashboard", + "name": "Project", + "dashboard_name": "Project", + "charts": [ + { "chart": "Project Summary", "width": "Full" } + ] + }] + +def get_charts(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + + return [ + { + 'doctype': 'Dashboard Chart', + 'name': 'Project Summary', + 'chart_name': 'Project Summary', + 'chart_type': 'Report', + 'report_name': 'Project Summary', + 'is_public': 1, + 'filters_json': json.dumps({"company": company.name, "status": "Open"}), + 'type': 'Bar', + 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', + } + ] \ No newline at end of file From 318affedb8d63fc37544c59ca79caba581f8efb3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 20:26:12 +0530 Subject: [PATCH 088/410] fix: divide by zero error --- erpnext/projects/report/project_summary/project_summary.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 66d68bda06a..a20d7f25a3a 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -112,6 +112,9 @@ def get_chart_data(data): } def get_report_summary(data): + if not data: + return None + avg_completion = sum([project.percent_complete for project in data]) / len(data) total = sum([project.total_tasks for project in data]) total_overdue = sum([project.overdue_tasks for project in data]) From e16498590377487967ee122ac6fc5590fdf5d85b Mon Sep 17 00:00:00 2001 From: SDLyu Date: Thu, 7 May 2020 16:49:29 +0800 Subject: [PATCH 089/410] Create taiwan.html --- erpnext/regional/address_template/templates/taiwan.html | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 erpnext/regional/address_template/templates/taiwan.html diff --git a/erpnext/regional/address_template/templates/taiwan.html b/erpnext/regional/address_template/templates/taiwan.html new file mode 100644 index 00000000000..43051afc778 --- /dev/null +++ b/erpnext/regional/address_template/templates/taiwan.html @@ -0,0 +1,4 @@ +{{ country }}
{% if pincode %}{{ pincode }}
{% endif -%}{{ county }}{{ city }}{{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}{% endif -%} +{% if phone %}Phone: {{ phone }}
{% endif -%} +{% if fax %}Fax: {{ fax }}
{% endif -%} +{% if email_id %}Email: {{ email_id }}
{% endif -%} From b2eb4ee0f98b4cce7f4f32edb6cdcd9b19c7c08a Mon Sep 17 00:00:00 2001 From: SDLyu Date: Thu, 7 May 2020 17:09:17 +0800 Subject: [PATCH 090/410] Change
position Change
position for better format --- erpnext/regional/address_template/templates/taiwan.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/address_template/templates/taiwan.html b/erpnext/regional/address_template/templates/taiwan.html index 43051afc778..1715bea48ae 100644 --- a/erpnext/regional/address_template/templates/taiwan.html +++ b/erpnext/regional/address_template/templates/taiwan.html @@ -1,4 +1,4 @@ -{{ country }}
{% if pincode %}{{ pincode }}
{% endif -%}{{ county }}{{ city }}{{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}{% endif -%} -{% if phone %}Phone: {{ phone }}
{% endif -%} -{% if fax %}Fax: {{ fax }}
{% endif -%} -{% if email_id %}Email: {{ email_id }}
{% endif -%} +{{ country }}
{% if pincode %}{{ pincode }}
{% endif -%}{{ county }}{{ city }}{{ address_line1 }}{% if address_line2 %}{{ address_line2 }}{% endif -%} +{% if phone %}
Phone: {{ phone }}{% endif -%} +{% if fax %}
Fax: {{ fax }}{% endif -%} +{% if email_id %}
Email: {{ email_id }}{% endif -%} From 52189cba861e0d88cf9847f53bd5c125f683f824 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Thu, 7 May 2020 17:49:32 +0800 Subject: [PATCH 091/410] style: Improve formatting This commit improves indentations and makes sql queries more readable. --- .../budget_variance_report.py | 418 ++++++++++++------ 1 file changed, 284 insertions(+), 134 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 39e218bfad2..f286a457576 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -2,187 +2,337 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +from six import iteritems + import frappe from frappe import _ from frappe.utils import flt from frappe.utils import formatdate + from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges -from six import iteritems -from pprint import pprint + def execute(filters=None): - if not filters: filters = {} + if not filters: + filters = {} - columns = get_columns(filters) - if filters.get("budget_against_filter"): - dimensions = filters.get("budget_against_filter") - else: - dimensions = get_cost_centers(filters) + columns = get_columns(filters) - period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"]) - cam_map = get_dimension_account_month_map(filters) + if filters.get("budget_against_filter"): + dimensions = filters.get("budget_against_filter") + else: + dimensions = get_cost_centers(filters) - data = [] - for dimension in dimensions: - dimension_items = cam_map.get(dimension) - if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value + period_month_ranges = get_period_month_ranges( + filters["period"], filters["from_fiscal_year"] + ) - period_data[0] += last_total + cam_map = get_dimension_account_month_map(filters) - if(filters.get("show_cumulative")): - last_total = period_data[0] - period_data[1] + data = [] + for dimension in dimensions: + dimension_items = cam_map.get(dimension) + if dimension_items: + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate( + ["target", "actual", "variance"] + ): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly" : - row += totals - data.append(row) + period_data[0] += last_total + + if filters.get("show_cumulative"): + last_total = period_data[0] - period_data[1] + + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly": + row += totals + data.append(row) + + return columns, data - return columns, data def get_columns(filters): - columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"] + columns = [ + _(filters.get("budget_against")) + + ":Link/%s:150" % (filters.get("budget_against")), + _("Account") + ":Link/Account:150", + ] - group_months = False if filters["period"] == "Monthly" else True + group_months = False if filters["period"] == "Monthly" else True - fiscal_year = get_fiscal_years(filters) + fiscal_year = get_fiscal_years(filters) - for year in fiscal_year: - for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): - if filters["period"] == "Yearly": - labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])] - for label in labels: - columns.append(label+":Float:150") - else: - for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]: - if group_months: - label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM")) - else: - label = label % formatdate(from_date, format_string="MMM") + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters["period"] == "Yearly": + labels = [ + _("Budget") + " " + str(year[0]), + _("Actual ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]), + ] + for label in labels: + columns.append(label + ":Float:150") + else: + for label in [ + _("Budget") + " (%s)" + " " + str(year[0]), + _("Actual") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]), + ]: + if group_months: + label = label % ( + formatdate(from_date, format_string="MMM") + + "-" + + formatdate(to_date, format_string="MMM") + ) + else: + label = label % formatdate(from_date, format_string="MMM") - columns.append(label+":Float:150") + columns.append(label + ":Float:150") + + if filters["period"] != "Yearly": + return columns + [ + _("Total Budget") + ":Float:150", + _("Total Actual") + ":Float:150", + _("Total Variance") + ":Float:150", + ] + else: + return columns - if filters["period"] != "Yearly" : - return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150"] - else: - return columns def get_cost_centers(filters): - cond = "and 1=1" - if filters.get("budget_against") == "Cost Center": - cond = "order by lft" + order_by = "" + if filters.get("budget_against") == "Cost Center": + order_by = "order by lft" - if filters.get("budget_against") in ["Cost Center", "Project"]: - return frappe.db.sql_list("""select name from `tab{tab}` where company=%s - {cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company")) - else: - return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec + if filters.get("budget_against") in ["Cost Center", "Project"]: + return frappe.db.sql_list( + """ + select + name + from + `tab{tab}` + where + company = %s + {order_by}; + """.format( + tab=filters.get("budget_against"), order_by=order_by + ), + filters.get("company"), + ) + else: + return frappe.db.sql_list( + """ + select + name + from + `tab{tab}` + """.format( + tab=filters.get("budget_against") + ) + ) # nosec -#Get dimension & target details + +# Get dimension & target details def get_dimension_target_details(filters): - cond = "" - if filters.get("budget_against_filter"): - cond += " and b.{budget_against} in (%s)".format(budget_against = \ - frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter'))) + budget_against = frappe.scrub(filters.get("budget_against")) - return frappe.db.sql(""" - select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year - from `tabBudget` b, `tabBudget Account` ba - where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s - and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year - """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), - tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')), - as_dict=True) + cond = "" + if filters.get("budget_against_filter"): + cond += """ + and + b.{budget_against} in ( + %s + ) + """.format( + budget_against=budget_against + ) % ", ".join( + ["%s"] * len(filters.get("budget_against_filter")) + ) + + return frappe.db.sql( + """ + select + b.{budget_against} as name + from + `tabBudget` b + where + b.docstatus = 1 + and b.fiscal_year between %s and %s + and b.budget_against = %s + and b.company = %s + {cond} + order by + b.fiscal_year + """.format( + budget_against=budget_against, cond=cond + ), + tuple( + [ + filters.from_fiscal_year, + filters.to_fiscal_year, + filters.budget_against, + filters.company, + ] + + filters.get("budget_against_filter") + ), + as_dict=True, + ) -#Get target distribution details of accounts of cost center +# Get target distribution details of accounts of cost center def get_target_distribution_details(filters): - target_details = {} - for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation - from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md - where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1): - target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) + target_details = {} + for d in frappe.db.sql( + """ + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s + and %s + order by md.fiscal_year + """, + (filters.from_fiscal_year, filters.to_fiscal_year), + as_dict=1, + ): + target_details.setdefault(d.name, {}).setdefault( + d.month, flt(d.percentage_allocation) + ) - return target_details + return target_details -#Get actual details from gl entry + +# Get actual details from gl entry def get_actual_details(name, filters): - cond = "1=1" - budget_against=filters.get("budget_against").replace(" ", "_").lower() + budget_against = frappe.scrub(filters.get("budget_against")) - if filters.get("budget_against") == "Cost Center": - cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) - cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt) + cond = "" + if filters.get("budget_against") == "Cost Center": + cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) + cond = """ + and lft >= '{lft}' + and rgt <= '{rgt}' + """.format( + lft=cc_lft, rgt=cc_rgt + ) - ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year, - MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against - from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b - where - b.name = ba.parent - and b.docstatus = 1 - and ba.account=gl.account - and b.{budget_against} = gl.{budget_against} - and gl.fiscal_year between %s and %s - and b.{budget_against}=%s - and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year - """.format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year), - (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1) + ac_details = frappe.db.sql( + """ + select + gl.account, + gl.debit, + gl.credit, + gl.fiscal_year, + MONTHNAME(gl.posting_date) as month_name, + b.{budget_against} as budget_against + from + `tabGL Entry` gl, + `tabBudget Account` ba, + `tabBudget` b + where + b.name = ba.parent + and b.docstatus = 1 + and ba.account=gl.account + and b.{budget_against} = gl.{budget_against} + and gl.fiscal_year between %s and %s + and b.{budget_against} = %s + and exists( + select + name + from + `tab{tab}` + where + name = gl.{budget_against} + {cond} + ) + group by + gl.name + order by gl.fiscal_year + """.format( + tab=filters.budget_against, budget_against=budget_against, cond=cond + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + as_dict=1, + ) - cc_actual_details = {} - for d in ac_details: - cc_actual_details.setdefault(d.account, []).append(d) + cc_actual_details = {} + for d in ac_details: + cc_actual_details.setdefault(d.account, []).append(d) + + return cc_actual_details - return cc_actual_details def get_dimension_account_month_map(filters): - import datetime - dimension_target_details = get_dimension_target_details(filters) - tdd = get_target_distribution_details(filters) + import datetime - cam_map = {} + dimension_target_details = get_dimension_target_details(filters) + tdd = get_target_distribution_details(filters) - for ccd in dimension_target_details: - actual_details = get_actual_details(ccd.budget_against, filters) + cam_map = {} - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime('%B') - cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\ - .setdefault(month, frappe._dict({ - "target": 0.0, "actual": 0.0 - })) + for ccd in dimension_target_details: + actual_details = get_actual_details(ccd.budget_against, filters) - tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] - month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \ - if ccd.monthly_distribution else 100.0/12 + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime("%B") + cam_map.setdefault(ccd.budget_against, {}).setdefault( + ccd.account, {} + ).setdefault(ccd.fiscal_year, {}).setdefault( + month, frappe._dict({"target": 0.0, "actual": 0.0}) + ) - tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] + month_percentage = ( + tdd.get(ccd.monthly_distribution, {}).get(month, 0) + if ccd.monthly_distribution + else 100.0 / 12 + ) - for ad in actual_details.get(ccd.account, []): - if ad.month_name == month: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) + tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + + for ad in actual_details.get(ccd.account, []): + if ad.month_name == month: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) + + return cam_map - return cam_map def get_fiscal_years(filters): - fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where - name between %(from_fiscal_year)s and %(to_fiscal_year)s""", - {'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]}) + fiscal_year = frappe.db.sql( + """ + select + name + from + `tabFiscal Year` + where + name between %(from_fiscal_year)s and %(to_fiscal_year)s + order by + year + """, + { + "from_fiscal_year": filters["from_fiscal_year"], + "to_fiscal_year": filters["to_fiscal_year"], + }, + ) - return fiscal_year + return fiscal_year From 2f81f754acc4c2db7ee4632e85b411a1b9736634 Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 7 May 2020 15:11:39 +0530 Subject: [PATCH 092/410] fix(crm): fix lead while updating contact details it use to throw error while updating contact details for lead "Next Contact Date cannot be in the past" is being solved --- erpnext/crm/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 38bf79e5fcb..95b19ec21ec 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -19,6 +19,5 @@ def update_lead_phone_numbers(contact, method): mobile_no = primary_mobile_nos[0] lead = frappe.get_doc("Lead", contact_lead) - lead.phone = phone - lead.mobile_no = mobile_no - lead.save() + lead.db_set("phone", phone) + lead.db_set("mobile_no", mobile_no) From fc0e45d79c2d07c2313264d019489fa901f6c6dc Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 7 May 2020 15:11:39 +0530 Subject: [PATCH 093/410] solved merge conflicts --- erpnext/crm/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 38bf79e5fcb..95b19ec21ec 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -19,6 +19,5 @@ def update_lead_phone_numbers(contact, method): mobile_no = primary_mobile_nos[0] lead = frappe.get_doc("Lead", contact_lead) - lead.phone = phone - lead.mobile_no = mobile_no - lead.save() + lead.db_set("phone", phone) + lead.db_set("mobile_no", mobile_no) From b3ee6314f9ef7937b96bf9c346c13161c9ca5226 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Thu, 7 May 2020 18:39:47 +0800 Subject: [PATCH 094/410] fix: Fix Budget Variance Report This commit fixes a bug in Budget Variance Report where it combines the actual expense amounts across different fiscal years. This was fixed by updating the function and queries for computing the actual expense amounts. --- .../budget_variance_report.py | 172 +++++++++++++----- 1 file changed, 127 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index f286a457576..1b110b0aacd 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -2,12 +2,12 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +import datetime from six import iteritems import frappe from frappe import _ -from frappe.utils import flt -from frappe.utils import formatdate +from frappe.utils import flt, formatdate from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges @@ -126,8 +126,8 @@ def get_cost_centers(filters): from `tab{tab}` where - company = %s - {order_by}; + company=%s + {order_by} """.format( tab=filters.get("budget_against"), order_by=order_by ), @@ -153,11 +153,11 @@ def get_dimension_target_details(filters): cond = "" if filters.get("budget_against_filter"): cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( + and + b.{budget_against} in ( + %s + ) + """.format( budget_against=budget_against ) % ", ".join( ["%s"] * len(filters.get("budget_against_filter")) @@ -165,7 +165,7 @@ def get_dimension_target_details(filters): return frappe.db.sql( """ - select + select distinct b.{budget_against} as name from `tabBudget` b @@ -198,19 +198,19 @@ def get_target_distribution_details(filters): target_details = {} for d in frappe.db.sql( """ - select - md.name, - mdp.month, - mdp.percentage_allocation - from - `tabMonthly Distribution Percentage` mdp, - `tabMonthly Distribution` md - where - mdp.parent = md.name - and md.fiscal_year between %s - and %s - order by md.fiscal_year - """, + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s + and %s + order by md.fiscal_year + """, (filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1, ): @@ -229,8 +229,8 @@ def get_actual_details(name, filters): if filters.get("budget_against") == "Cost Center": cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cond = """ - and lft >= '{lft}' - and rgt <= '{rgt}' + and lft >= \'{lft}\' + and rgt <= \'{rgt}\' """.format( lft=cc_lft, rgt=cc_rgt ) @@ -282,37 +282,44 @@ def get_actual_details(name, filters): def get_dimension_account_month_map(filters): - import datetime - dimension_target_details = get_dimension_target_details(filters) tdd = get_target_distribution_details(filters) cam_map = {} for ccd in dimension_target_details: - actual_details = get_actual_details(ccd.budget_against, filters) + accounts = get_accounts(ccd.name, filters) + actual_details = get_actual_details(ccd.name, filters) - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime("%B") - cam_map.setdefault(ccd.budget_against, {}).setdefault( - ccd.account, {} - ).setdefault(ccd.fiscal_year, {}).setdefault( - month, frappe._dict({"target": 0.0, "actual": 0.0}) - ) + for year in get_fiscal_years(filters): + year = year[0] + monthly_distribution = get_monthly_distribution(ccd.name, year, filters) + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime( + "%B" + ) # Get month string - tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] - month_percentage = ( - tdd.get(ccd.monthly_distribution, {}).get(month, 0) - if ccd.monthly_distribution - else 100.0 / 12 - ) + for account in accounts: + account = account[0] + cam_map.setdefault(ccd.name, {}).setdefault(account, {}).setdefault( + year, {} + ).setdefault(month, frappe._dict({"target": 0.0, "actual": 0.0})) - tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 + tav_dict = cam_map[ccd.name][account][year][month] - for ad in actual_details.get(ccd.account, []): - if ad.month_name == month: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) + month_percentage = ( + tdd.get(monthly_distribution, {}).get(month, 0) + if monthly_distribution + else 100.0 / 12 + ) + budget_amount = get_budget_amount(ccd.name, year, account, filters) + + tav_dict.target = flt(budget_amount) * month_percentage / 100 + + for ad in actual_details.get(account, []): + if ad.month_name == month and ad.fiscal_year == year: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) return cam_map @@ -336,3 +343,78 @@ def get_fiscal_years(filters): ) return fiscal_year + + +def get_accounts(name, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + accounts = frappe.db.sql( + """ + select + distinct(ba.account) + from + `tabBudget Account` ba + join + `tabBudget` b + on b.name = ba.parent + where + b.docstatus = 1 + and b.fiscal_year between %s and %s + and b.{budget_against} = %s + order by + ba.account + """.format( + budget_against=budget_against + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + ) + + return accounts + + +def get_monthly_distribution(name, year, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + monthly_distribution = frappe.db.sql( + """ + select + monthly_distribution + from + `tabBudget` + where + docstatus = 1 + and {budget_against} = %s + and fiscal_year = %s + """.format( + budget_against=budget_against + ), + (name, year), + ) + + return monthly_distribution[0][0] if monthly_distribution else None + + +def get_budget_amount(name, year, account, filters): + budget_against = frappe.scrub(filters.get("budget_against")) + + budget_amount = frappe.db.sql( + """ + select + ba.budget_amount + from + `tabBudget Account` ba + join + `tabBudget` b + on b.name = ba.parent + where + b.docstatus = 1 + and b.{budget_against} = %s + and b.fiscal_year = %s + and ba.account = %s + """.format( + budget_against=budget_against + ), + (name, year, account), + ) + + return budget_amount[0][0] if budget_amount else 0 From 2ba89ffd488b835bd288102c1fceed09f4bed83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrker=20Tunal=C4=B1?= Date: Thu, 7 May 2020 12:38:37 +0300 Subject: [PATCH 095/410] fix: Job Card submitted qty Update Operation Status function in work order was throwing exception without checking the "Overproduction Percentage For Work Order" setting. To submit Job Card qty for more than the Work Order's "To Manufacture Qty" we need to apply this fix. (cherry picked from commit 9bd6d119c405f2ace6c93d53647af140c6543542) --- erpnext/manufacturing/doctype/work_order/work_order.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 84bfab2f1d9..8301f30d837 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -421,6 +421,9 @@ class WorkOrder(Document): return holidays[holiday_list] def update_operation_status(self): + allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")) + max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage/100 * flt(self.qty)) + for d in self.get("operations"): if not d.completed_qty: d.status = "Pending" @@ -428,6 +431,8 @@ class WorkOrder(Document): d.status = "Work in Progress" elif flt(d.completed_qty) == flt(self.qty): d.status = "Completed" + elif flt(d.completed_qty) <= max_allowed_qty_for_wo: + d.status = "Completed" else: frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'")) From b1385f676a60fe13201399e7051d4c7b6cfcf878 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 7 May 2020 16:47:41 +0530 Subject: [PATCH 096/410] fix(lead): strip lead_name before splitting --- erpnext/crm/doctype/lead/lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 74b358219dc..ec7d14d6ae3 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -153,7 +153,7 @@ class Lead(SellingController): if not self.lead_name: self.set_lead_name() - names = self.lead_name.split(" ") + names = self.lead_name.strip().split(" ") if len(names) > 1: first_name, last_name = names[0], " ".join(names[1:]) else: From 31a747b98ad4220d6659523f9a53b5bb81ba9252 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 7 May 2020 17:38:00 +0530 Subject: [PATCH 097/410] fix: Query enhancement, cleanup, added extra filter - Query changes as requested - Moved chart generation from js to py - Added Supplier Multiselect filter --- .../quoted_item_comparison.js | 64 ++--------- .../quoted_item_comparison.py | 101 ++++++++++++++---- 2 files changed, 88 insertions(+), 77 deletions(-) diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js index fe4abd8c9c5..a76ffeec2ec 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js @@ -16,7 +16,7 @@ frappe.query_reports["Quoted Item Comparison"] = { default: "", options: "Item", label: __("Item"), - fieldname: "item", + fieldname: "item_code", fieldtype: "Link", get_query: () => { let quote = frappe.query_report.get_filter_value('supplier_quotation'); @@ -36,6 +36,14 @@ frappe.query_reports["Quoted Item Comparison"] = { } } }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Supplier', txt); + } + }, { fieldtype: "Link", label: __("Supplier Quotation"), @@ -58,60 +66,6 @@ frappe.query_reports["Quoted Item Comparison"] = { } ], - prepare_chart_data: (result) => { - let supplier_wise_map = {}, data_points_map = {}; - let qty_list = result.map(res => res.qty); - qty_list.sort(); - qty_list = new Set(qty_list); - - // create supplier wise map like in Report - for (let res of result) { - if (!(res.supplier in supplier_wise_map)) { - supplier_wise_map[res.supplier] = {}; - } - supplier_wise_map[res.supplier][res.qty] = res.price; - } - - // create datapoints for each qty - for (let supplier of Object.keys(supplier_wise_map)) { - let row = supplier_wise_map[supplier]; - for (let qty of qty_list) { - if (!data_points_map[qty]) { - data_points_map[qty] = []; - } - if (row[qty]) { - data_points_map[qty].push(row[qty]); - } - else { - data_points_map[qty].push(null); - } - } - } - - let dataset = []; - qty_list.forEach((qty) => { - let datapoints = { - 'name': __('Price for Qty ') + qty, - 'values': data_points_map[qty] - } - dataset.push(datapoints); - }); - return dataset; - }, - - get_chart_data: function (columns, result) { - let suppliers = result.filter(d => d.supplier_name).map(res => res.supplier_name); - let dataset = frappe.query_reports["Quoted Item Comparison"].prepare_chart_data(result); - - return { - data: { - labels: suppliers, - datasets: dataset - }, - type: 'bar' - } - }, - onload: (report) => { // Create a button for setting the default supplier report.page.add_inner_button(__("Select Default Supplier"), () => { diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py index fd7a7311983..a33867a525e 100644 --- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py +++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py @@ -9,21 +9,33 @@ from collections import defaultdict from erpnext.setup.utils import get_exchange_rate def execute(filters=None): + if not filters: + return [], [] + conditions = get_conditions(filters) - data = get_data(filters, conditions) + supplier_quotation_data = get_data(filters, conditions) columns = get_columns() - return columns, data + + data, chart_data = prepare_data(supplier_quotation_data) + + return columns, data, None, chart_data + +def get_conditions(filters): + conditions = "" + if filters.get("supplier_quotation"): + conditions += " AND sqi.parent = %(supplier_quotation)s" + + if filters.get("request_for_quotation"): + conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s" + + if filters.get("supplier"): + conditions += " AND sq.supplier in %(supplier)s" + return conditions def get_data(filters, conditions): - out, suppliers = [], [] - item = filters.get("item") - - if not item: + if not filters.get("item_code"): return [] - company_currency = frappe.db.get_default("currency") - float_precision = cint(frappe.db.get_default("float_precision")) or 2 - supplier_quotation_data = frappe.db.sql("""SELECT sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation, sq.supplier @@ -31,17 +43,27 @@ def get_data(filters, conditions): `tabSupplier Quotation Item` sqi, `tabSupplier Quotation` sq WHERE - sqi.item_code = '{0}' + sqi.item_code = %(item_code)s AND sqi.parent = sq.name AND sqi.docstatus < 2 - AND sq.company = '{1}' + AND sq.company = %(company)s AND sq.status != 'Expired' - {2}""".format(item, filters.get("company"), conditions), as_dict=1) + {0}""".format(conditions), filters, as_dict=1) + return supplier_quotation_data + +def prepare_data(supplier_quotation_data): + out, suppliers, qty_list = [], [], [] supplier_wise_map = defaultdict(list) + supplier_qty_price_map = {} + + company_currency = frappe.db.get_default("currency") + float_precision = cint(frappe.db.get_default("float_precision")) or 2 for data in supplier_quotation_data: + supplier = data.get("supplier") supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency") + if supplier_currency: exchange_rate = get_exchange_rate(supplier_currency, company_currency) else: @@ -53,29 +75,64 @@ def get_data(filters, conditions): "price": flt(data.get("rate") * exchange_rate, float_precision), "uom": data.get("uom"), "request_for_quotation": data.get("request_for_quotation"), - "supplier": data.get("supplier") # used for chart generation } - supplier_wise_map[data.supplier].append(row) - suppliers.append(data.supplier) + # map for report view of form {'supplier1':[{},{},...]} + supplier_wise_map[supplier].append(row) - suppliers = set(suppliers) + # map for chart preparation of the form {'supplier1': {'qty': 'price'}} + if not supplier in supplier_qty_price_map: + supplier_qty_price_map[supplier] = {} + supplier_qty_price_map[supplier][row["qty"]] = row["price"] + suppliers.append(supplier) + qty_list.append(data.get("qty")) + + suppliers = list(set(suppliers)) + qty_list = list(set(qty_list)) + + # final data format for report view for supplier in suppliers: supplier_wise_map[supplier][0].update({"supplier_name": supplier}) for entry in supplier_wise_map[supplier]: out.append(entry) - return out + chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map) -def get_conditions(filters): - conditions = "" + return out, chart_data - if filters.get("request_for_quotation"): - conditions += " AND sqi.request_for_quotation = '{0}' ".format(filters.get("request_for_quotation")) +def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map): + data_points_map = {} + qty_list.sort() - return conditions + # create qty wise values map of the form {'qty1':[value1, value2]} + for supplier in suppliers: + entry = supplier_qty_price_map[supplier] + for qty in qty_list: + if not qty in data_points_map: + data_points_map[qty] = [] + if qty in entry: + data_points_map[qty].append(entry[qty]) + else: + data_points_map[qty].append(None) + dataset = [] + for qty in qty_list: + datapoints = { + "name": _("Price for Qty ") + str(qty), + "values": data_points_map[qty] + } + dataset.append(datapoints) + + chart_data = { + "data": { + "labels": suppliers, + "datasets": dataset + }, + "type": "bar" + } + + return chart_data def get_columns(): columns = [{ From ebf3b78b420acc40156bec117f080952b8137366 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 7 May 2020 19:20:16 +0530 Subject: [PATCH 098/410] renaming LMS to Learning Management System --- .../education_settings/education_settings.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json index 967a030fd2a..0e548dbf774 100644 --- a/erpnext/education/doctype/education_settings/education_settings.json +++ b/erpnext/education/doctype/education_settings/education_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2017-04-05 13:33:04.519313", "doctype": "DocType", "editable_grid": 1, @@ -42,12 +43,14 @@ "fieldtype": "Column Break" }, { + "default": "0", "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", "fieldname": "validate_batch", "fieldtype": "Check", "label": "Validate Batch for Students in Student Group" }, { + "default": "0", "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", "fieldname": "validate_course", "fieldtype": "Check", @@ -74,13 +77,13 @@ { "fieldname": "web_academy_settings_section", "fieldtype": "Section Break", - "label": "LMS Settings" + "label": "Learning Management System Settings" }, { "depends_on": "eval: doc.enable_lms", "fieldname": "portal_title", "fieldtype": "Data", - "label": "LMS Title" + "label": "Learning Management System Title" }, { "depends_on": "eval: doc.enable_lms", @@ -89,9 +92,10 @@ "label": "Description" }, { + "default": "0", "fieldname": "enable_lms", "fieldtype": "Check", - "label": "Enable LMS" + "label": "Enable Learning Management System" }, { "default": "0", @@ -102,7 +106,8 @@ } ], "issingle": 1, - "modified": "2019-05-13 18:36:13.127563", + "links": [], + "modified": "2020-05-07 19:18:10.639356", "modified_by": "Administrator", "module": "Education", "name": "Education Settings", @@ -141,4 +146,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From 383a94d7302d0f8da179f0cdc0bf2c0b76c3bd44 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 7 May 2020 23:32:53 +0530 Subject: [PATCH 099/410] fix: delete old appointment analytics tree grid report --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ce0e4ac4711..525593327e9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") From 8b526977234881cb3315af16b5f7ca5fc864fb31 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 8 May 2020 02:23:28 +0530 Subject: [PATCH 100/410] Payment Order not allowing to create Payment Entry --- erpnext/accounts/doctype/payment_order/payment_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py index 3f3174a69b6..7ecdc41d034 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.py +++ b/erpnext/accounts/doctype/payment_order/payment_order.py @@ -80,7 +80,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None): paid_amt += d.amount je.append('accounts', { - 'account': doc.references[0].account, + 'account': doc.account, 'credit_in_account_currency': paid_amt }) From 5b1a0fe00ebb9af0c91894dfee5622b04a29e7d7 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 8 May 2020 13:06:50 +0530 Subject: [PATCH 101/410] fix: bump erpnext develop to version 13-dev (#21651) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 786b9cfd164..38d8a62f07f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.0.0-dev' +__version__ = '13.0.0-dev' def get_default_company(user=None): '''Get default company for user''' From 7d4ccdf24006f70bef35b3a4882773df47d989d2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 8 May 2020 13:27:25 +0530 Subject: [PATCH 102/410] fix: wrong fieldname branch_code in add_fetch --- .../doctype/payment_request/payment_request.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 97ae5ffde36..7508683c080 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2015-12-15 22:23:24.745065", "doctype": "DocType", @@ -210,13 +211,14 @@ "label": "IBAN" }, { - "fetch_from": "bank_account.branch_code", + "fetch_from": "bank.branch_code", + "fetch_if_empty": 1, "fieldname": "branch_code", "fieldtype": "Read Only", "label": "Branch Code" }, { - "fetch_from": "bank_account.swift_number", + "fetch_from": "bank.swift_number", "fieldname": "swift_number", "fieldtype": "Read Only", "label": "SWIFT Number" @@ -348,7 +350,8 @@ } ], "is_submittable": 1, - "modified": "2020-03-28 16:07:31.960798", + "links": [], + "modified": "2020-05-08 10:23:02.815237", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", From da41724d9da94acf82c44912b3352535ca304fb9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 8 May 2020 15:12:08 +0530 Subject: [PATCH 103/410] feat: save shipping address to woocommerce customer --- .../connectors/woocommerce_connection.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 618865200cf..54fa6085d03 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -49,12 +49,13 @@ def _order(*args, **kwargs): if event == "created": sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") + raw_shipping_data = order.get("shipping") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") - link_customer_and_address(raw_billing_data, customer_name) + link_customer_and_address(raw_billing_data, raw_shipping_data, 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): +def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email}) if not customer_exists: @@ -68,38 +69,48 @@ def link_customer_and_address(raw_billing_data, customer_name): customer.customer_name = customer_name customer.woocommerce_email = customer_woo_com_email customer.flags.ignore_mandatory = True - customer.save() + customer.save() if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email}) + billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) + shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) else: - address = frappe.new_doc("Address") + billing_address = create_address(raw_billing_data, customer, "Billing") + shipping_address = create_address(raw_shipping_data, customer, "Shipping") - address.address_line1 = raw_billing_data.get("address_1", "Not Provided") - address.address_line2 = raw_billing_data.get("address_2", "Not Provided") - address.city = raw_billing_data.get("city", "Not Provided") - address.woocommerce_email = customer_woo_com_email - address.address_type = "Billing" - address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()}) - address.state = raw_billing_data.get("state") - address.pincode = raw_billing_data.get("postcode") - address.phone = raw_billing_data.get("phone") - address.email_id = customer_woo_com_email + if customer_exists: + rename_address(billing_address, customer) + rename_address(shipping_address, customer) + +def create_address(raw_data, customer, address_type): + address = frappe.new_doc("Address") + + address.address_line1 = raw_data.get("address_1", "Not Provided") + address.address_line2 = raw_data.get("address_2", "Not Provided") + address.city = raw_data.get("city", "Not Provided") + address.woocommerce_email = customer.woocommerce_email + address.address_type = address_type + address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()}) + address.state = raw_data.get("state") + address.pincode = raw_data.get("postcode") + address.phone = raw_data.get("phone") + address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", "link_name": customer.customer_name }) + address.flags.ignore_mandatory = True address = address.save() - if customer_exists: - old_address_title = address.name - new_address_title = customer.customer_name + "-billing" - address.address_title = customer.customer_name - address.save() +def rename_address(address, customer): + old_address_title = address.name + new_address_title = customer.customer_name + "-" + address.address_type + address.address_title = customer.customer_name + address.save() - frappe.rename_doc("Address", old_address_title, new_address_title) + frappe.rename_doc("Address", old_address_title, new_address_title) def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: @@ -111,7 +122,7 @@ def link_items(items_list, woocommerce_settings, sys_lang): else: #Create Item item = frappe.new_doc("Item") - + item.item_name = item_data.get("name") item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") @@ -171,7 +182,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account) add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account) - + def add_tax_details(sales_order, price, desc, tax_account_head): sales_order.append("taxes", { "charge_type":"Actual", From a788ab281e8daf24d57ede10325b0f88966d086d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 8 May 2020 15:13:36 +0530 Subject: [PATCH 104/410] fix: typo in function name --- erpnext/tests/test_woocommerce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py index ce0f47d685f..df715ab2027 100644 --- a/erpnext/tests/test_woocommerce.py +++ b/erpnext/tests/test_woocommerce.py @@ -24,7 +24,7 @@ class TestWoocommerce(unittest.TestCase): woo_settings.creation_user = "Administrator" woo_settings.save(ignore_permissions=True) - def test_sales_order_for_woocommerece(self): + def test_sales_order_for_woocommerce(self): frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} order() From e29283630b5b36e7d2eecbeff29726826d5eaba2 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Fri, 8 May 2020 17:45:08 +0800 Subject: [PATCH 105/410] fix: Zero threshold in Tax Withholding Category This fixes a bug in Tax Withholding Category where if it has a threshold of 0, it doesn't apply to Purchase Invoices. The bug was fixed by updating the condition. --- .../tax_withholding_category/tax_withholding_category.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index dd6b4fdc603..818e56633a6 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -162,8 +162,7 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date) supplier_credit_amount -= debit_note_amount - if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold) - or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)): + if supplier_credit_amount >= tax_details.get('threshold', 0) or supplier_credit_amount >= tax_details.get('cumulative_threshold', 0): if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total, ldc.certificate_limit): @@ -225,4 +224,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, certificate_limit > deducted_amount): valid = True - return valid \ No newline at end of file + return valid From 9d81f97fe677d0ac92064fc9a4e37bf4f5d4a6a0 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Fri, 8 May 2020 18:01:18 +0800 Subject: [PATCH 106/410] fix: Tax Withholding Category Description default This commit fixes a bug that happens when a Purchase Invoice uses a Tax Withholding Category without a category_name. The category_name is used as the tax description which is a required field. The bug was fixed by using the Tax Withholding Category's name as the description if the category_name is empty. --- .../tax_withholding_category/tax_withholding_category.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index dd6b4fdc603..e904a681f66 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name + "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): @@ -225,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, certificate_limit > deducted_amount): valid = True - return valid \ No newline at end of file + return valid From 074ea2f882640fce6848e3c69754d6584002489b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 8 May 2020 15:33:35 +0530 Subject: [PATCH 107/410] fix: local variable 'lft' referenced before assignment --- erpnext/stock/doctype/warehouse/warehouse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index eb4867d2cf3..cd86be31150 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -177,7 +177,7 @@ def convert_to_group_or_ledger(): return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger() def get_child_warehouses(warehouse): - lft, rgt = frappe.get_cached_value("Warehouse", warehouse, [lft, rgt]) + lft, rgt = frappe.get_cached_value("Warehouse", warehouse, ["lft", "rgt"]) return frappe.db.sql_list("""select name from `tabWarehouse` where lft >= %s and rgt <= %s""", (lft, rgt)) From 65123d2adcff62ff01f99b043f278b1bfdbf3f89 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 8 May 2020 16:30:46 +0530 Subject: [PATCH 108/410] fix: Item Barcode stays the same after updation. --- erpnext/stock/doctype/item/item.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index c62b3ab583d..4cc50bba9ea 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -572,6 +572,13 @@ class Item(WebsiteGenerator): frappe.throw(_("Barcode {0} is not a valid {1} code").format( item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode) + if item_barcode.barcode != item_barcode.name: + # if barcode is getting updated , the row name has to reset. + # Delete previous old row doc and re-enter row as if new to reset name in db. + item_barcode.set("__islocal", True) + item_barcode.name = None + frappe.delete_doc("Item Barcode", item_barcode.name) + def validate_warehouse_for_reorder(self): '''Validate Reorder level table for duplicate and conditional mandatory''' warehouse = [] From be6eb201b71150202f3c63fa2c632a5a9594cd95 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 8 May 2020 17:33:21 +0530 Subject: [PATCH 109/410] Appending Email and Phone in Child Table --- erpnext/selling/doctype/customer/customer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d0db6d62a04..3d172ac7a22 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -164,9 +164,11 @@ class Customer(TransactionBase): contact.phone = lead.phone contact.mobile_no = lead.mobile_no contact.is_primary_contact = 1 - contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) - contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.append('links', dict(link_doctype='Customer', link_name=self.name)) + if lead.email_id: + contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1)) + if lead.mobile_no: + contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1)) contact.flags.ignore_permissions = self.flags.ignore_permissions contact.autoname() if not frappe.db.exists("Contact", contact.name): From 8d9f86f78113de8dce8b4b8b193c7ab23430a143 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 8 May 2020 19:45:57 +0530 Subject: [PATCH 110/410] fix: Gross and net profit report fix --- .../gross_and_net_profit_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 1c458107d40..fb07472b22b 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -9,8 +9,8 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c import copy def execute(filters=None): - period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, - filters.periodicity, filters.accumulated_values, filters.company) + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, '', '', + 'Fiscal Year', filters.periodicity, filters.accumulated_values, filters.company) columns, data = [], [] From 3392e6b80c9c8105e4ebca00d0c6e164f12baa84 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 8 May 2020 19:53:09 +0530 Subject: [PATCH 111/410] fix: User filters instead of hardcoded values --- .../gross_and_net_profit_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index fb07472b22b..714e48d279b 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -9,8 +9,8 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c import copy def execute(filters=None): - period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, '', '', - 'Fiscal Year', filters.periodicity, filters.accumulated_values, filters.company) + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.period_start_date, + filters.period_end_date, filters.filter_based_on, filters.periodicity, filters.accumulated_values, filters.company) columns, data = [], [] From cb1376b036edb7d3161c331cdf4f7451a2d97a3e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 9 May 2020 12:05:33 +0530 Subject: [PATCH 112/410] chore: verbose error message for coa recursion --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index d9c5852a6ed..462685f5e71 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -113,7 +113,7 @@ class TallyMigration(Document): try: coa = traverse({}, children, roots, roots, group_set) except RecursionError: - self.log() + self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name")) for account in coa: coa[account]["root_type"] = root_type_map[account] From bc058558052a9d3af7f151347f7af018aa403002 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Sat, 9 May 2020 16:02:11 +0800 Subject: [PATCH 113/410] fix: Simplify get_dimension_account_month_map This commit updates get_dimension_account_month_map to no longer show the actual expense when there is no budget. This also removes the other functions and queries related to it. Spaces are also converted to tabs. --- .../budget_variance_report.py | 500 ++++++++---------- 1 file changed, 209 insertions(+), 291 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 1b110b0aacd..4d5892a9132 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -13,164 +13,165 @@ from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ def execute(filters=None): - if not filters: - filters = {} + if not filters: + filters = {} - columns = get_columns(filters) + columns = get_columns(filters) + if filters.get("budget_against_filter"): + dimensions = filters.get("budget_against_filter") + else: + dimensions = get_cost_centers(filters) - if filters.get("budget_against_filter"): - dimensions = filters.get("budget_against_filter") - else: - dimensions = get_cost_centers(filters) + period_month_ranges = get_period_month_ranges( + filters["period"], filters["from_fiscal_year"] + ) + cam_map = get_dimension_account_month_map(filters) - period_month_ranges = get_period_month_ranges( - filters["period"], filters["from_fiscal_year"] - ) + data = [] + for dimension in dimensions: + dimension_items = cam_map.get(dimension) + if dimension_items: + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate( + ["target", "actual", "variance"] + ): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value - cam_map = get_dimension_account_month_map(filters) + period_data[0] += last_total - data = [] - for dimension in dimensions: - dimension_items = cam_map.get(dimension) - if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate( - ["target", "actual", "variance"] - ): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value + if filters.get("show_cumulative"): + last_total = period_data[0] - period_data[1] - period_data[0] += last_total + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly": + row += totals + data.append(row) - if filters.get("show_cumulative"): - last_total = period_data[0] - period_data[1] - - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly": - row += totals - data.append(row) - - return columns, data + return columns, data def get_columns(filters): - columns = [ - _(filters.get("budget_against")) - + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150", - ] + columns = [ + _(filters.get("budget_against")) + + ":Link/%s:150" % (filters.get("budget_against")), + _("Account") + ":Link/Account:150", + ] - group_months = False if filters["period"] == "Monthly" else True + group_months = False if filters["period"] == "Monthly" else True - fiscal_year = get_fiscal_years(filters) + fiscal_year = get_fiscal_years(filters) - for year in fiscal_year: - for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): - if filters["period"] == "Yearly": - labels = [ - _("Budget") + " " + str(year[0]), - _("Actual ") + " " + str(year[0]), - _("Variance ") + " " + str(year[0]), - ] - for label in labels: - columns.append(label + ":Float:150") - else: - for label in [ - _("Budget") + " (%s)" + " " + str(year[0]), - _("Actual") + " (%s)" + " " + str(year[0]), - _("Variance") + " (%s)" + " " + str(year[0]), - ]: - if group_months: - label = label % ( - formatdate(from_date, format_string="MMM") - + "-" - + formatdate(to_date, format_string="MMM") - ) - else: - label = label % formatdate(from_date, format_string="MMM") + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters["period"] == "Yearly": + labels = [ + _("Budget") + " " + str(year[0]), + _("Actual ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]), + ] + for label in labels: + columns.append(label + ":Float:150") + else: + for label in [ + _("Budget") + " (%s)" + " " + str(year[0]), + _("Actual") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]), + ]: + if group_months: + label = label % ( + formatdate(from_date, format_string="MMM") + + "-" + + formatdate(to_date, format_string="MMM") + ) + else: + label = label % formatdate(from_date, format_string="MMM") - columns.append(label + ":Float:150") + columns.append(label + ":Float:150") - if filters["period"] != "Yearly": - return columns + [ - _("Total Budget") + ":Float:150", - _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150", - ] - else: - return columns + if filters["period"] != "Yearly": + return columns + [ + _("Total Budget") + ":Float:150", + _("Total Actual") + ":Float:150", + _("Total Variance") + ":Float:150", + ] + else: + return columns def get_cost_centers(filters): - order_by = "" - if filters.get("budget_against") == "Cost Center": - order_by = "order by lft" + order_by = "" + if filters.get("budget_against") == "Cost Center": + order_by = "order by lft" - if filters.get("budget_against") in ["Cost Center", "Project"]: - return frappe.db.sql_list( - """ + if filters.get("budget_against") in ["Cost Center", "Project"]: + return frappe.db.sql_list( + """ select name from `tab{tab}` where - company=%s + company = %s {order_by} """.format( - tab=filters.get("budget_against"), order_by=order_by - ), - filters.get("company"), - ) - else: - return frappe.db.sql_list( - """ + tab=filters.get("budget_against"), order_by=order_by + ), + filters.get("company"), + ) + else: + return frappe.db.sql_list( + """ select name from `tab{tab}` """.format( - tab=filters.get("budget_against") - ) - ) # nosec + tab=filters.get("budget_against") + ) + ) # nosec # Get dimension & target details def get_dimension_target_details(filters): - budget_against = frappe.scrub(filters.get("budget_against")) + budget_against = frappe.scrub(filters.get("budget_against")) + cond = "" + if filters.get("budget_against_filter"): + cond += """ + and + b.{budget_against} in ( + %s + ) + """.format( + budget_against=budget_against + ) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) - cond = "" - if filters.get("budget_against_filter"): - cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( - budget_against=budget_against - ) % ", ".join( - ["%s"] * len(filters.get("budget_against_filter")) - ) - - return frappe.db.sql( - """ - select distinct - b.{budget_against} as name + return frappe.db.sql( + """ + select + b.{budget_against} as budget_against, + b.monthly_distribution, + ba.account, + ba.budget_amount, + b.fiscal_year from - `tabBudget` b + `tabBudget` b, + `tabBudget Account` ba where - b.docstatus = 1 + b.name = ba.parent + and b.docstatus = 1 and b.fiscal_year between %s and %s and b.budget_against = %s and b.company = %s @@ -178,65 +179,66 @@ def get_dimension_target_details(filters): order by b.fiscal_year """.format( - budget_against=budget_against, cond=cond - ), - tuple( - [ - filters.from_fiscal_year, - filters.to_fiscal_year, - filters.budget_against, - filters.company, - ] - + filters.get("budget_against_filter") - ), - as_dict=True, - ) + budget_against=budget_against, + cond=cond, + ), + tuple( + [ + filters.from_fiscal_year, + filters.to_fiscal_year, + filters.budget_against, + filters.company, + ] + + filters.get("budget_against_filter") + ), + as_dict=True, + ) # Get target distribution details of accounts of cost center def get_target_distribution_details(filters): - target_details = {} - for d in frappe.db.sql( - """ - select - md.name, - mdp.month, - mdp.percentage_allocation - from - `tabMonthly Distribution Percentage` mdp, - `tabMonthly Distribution` md - where - mdp.parent = md.name - and md.fiscal_year between %s - and %s - order by md.fiscal_year - """, - (filters.from_fiscal_year, filters.to_fiscal_year), - as_dict=1, - ): - target_details.setdefault(d.name, {}).setdefault( - d.month, flt(d.percentage_allocation) - ) + target_details = {} + for d in frappe.db.sql( + """ + select + md.name, + mdp.month, + mdp.percentage_allocation + from + `tabMonthly Distribution Percentage` mdp, + `tabMonthly Distribution` md + where + mdp.parent = md.name + and md.fiscal_year between %s and %s + order by + md.fiscal_year + """, + (filters.from_fiscal_year, filters.to_fiscal_year), + as_dict=1, + ): + target_details.setdefault(d.name, {}).setdefault( + d.month, flt(d.percentage_allocation) + ) - return target_details + return target_details # Get actual details from gl entry def get_actual_details(name, filters): - budget_against = frappe.scrub(filters.get("budget_against")) + budget_against = frappe.scrub(filters.get("budget_against")) - cond = "" - if filters.get("budget_against") == "Cost Center": - cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) - cond = """ - and lft >= \'{lft}\' - and rgt <= \'{rgt}\' + cond = "" + if filters.get("budget_against") == "Cost Center": + cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) + cond = """ + and lft >= "{lft}" + and rgt <= "{rgt}" """.format( - lft=cc_lft, rgt=cc_rgt - ) + lft=cc_lft, rgt=cc_rgt + ) - ac_details = frappe.db.sql( - """ + ac_details = frappe.db.sql( + """ select gl.account, gl.debit, @@ -268,65 +270,56 @@ def get_actual_details(name, filters): gl.name order by gl.fiscal_year """.format( - tab=filters.budget_against, budget_against=budget_against, cond=cond - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - as_dict=1, - ) + tab=filters.budget_against, budget_against=budget_against, cond=cond + ), + (filters.from_fiscal_year, filters.to_fiscal_year, name), + as_dict=1, + ) - cc_actual_details = {} - for d in ac_details: - cc_actual_details.setdefault(d.account, []).append(d) + cc_actual_details = {} + for d in ac_details: + cc_actual_details.setdefault(d.account, []).append(d) - return cc_actual_details + return cc_actual_details def get_dimension_account_month_map(filters): - dimension_target_details = get_dimension_target_details(filters) - tdd = get_target_distribution_details(filters) + dimension_target_details = get_dimension_target_details(filters) + tdd = get_target_distribution_details(filters) - cam_map = {} + cam_map = {} - for ccd in dimension_target_details: - accounts = get_accounts(ccd.name, filters) - actual_details = get_actual_details(ccd.name, filters) + for ccd in dimension_target_details: + actual_details = get_actual_details(ccd.budget_against, filters) - for year in get_fiscal_years(filters): - year = year[0] - monthly_distribution = get_monthly_distribution(ccd.name, year, filters) - for month_id in range(1, 13): - month = datetime.date(2013, month_id, 1).strftime( - "%B" - ) # Get month string + for month_id in range(1, 13): + month = datetime.date(2013, month_id, 1).strftime("%B") + cam_map.setdefault(ccd.budget_against, {}).setdefault( + ccd.account, {} + ).setdefault(ccd.fiscal_year, {}).setdefault( + month, frappe._dict({"target": 0.0, "actual": 0.0}) + ) - for account in accounts: - account = account[0] - cam_map.setdefault(ccd.name, {}).setdefault(account, {}).setdefault( - year, {} - ).setdefault(month, frappe._dict({"target": 0.0, "actual": 0.0})) + tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month] + month_percentage = ( + tdd.get(ccd.monthly_distribution, {}).get(month, 0) + if ccd.monthly_distribution + else 100.0 / 12 + ) - tav_dict = cam_map[ccd.name][account][year][month] + tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100 - month_percentage = ( - tdd.get(monthly_distribution, {}).get(month, 0) - if monthly_distribution - else 100.0 / 12 - ) + for ad in actual_details.get(ccd.account, []): + if ad.month_name == month and ad.fiscal_year == ccd.fiscal_year: + tav_dict.actual += flt(ad.debit) - flt(ad.credit) - budget_amount = get_budget_amount(ccd.name, year, account, filters) - - tav_dict.target = flt(budget_amount) * month_percentage / 100 - - for ad in actual_details.get(account, []): - if ad.month_name == month and ad.fiscal_year == year: - tav_dict.actual += flt(ad.debit) - flt(ad.credit) - return cam_map + return cam_map def get_fiscal_years(filters): - fiscal_year = frappe.db.sql( - """ + fiscal_year = frappe.db.sql( + """ select name from @@ -336,85 +329,10 @@ def get_fiscal_years(filters): order by year """, - { - "from_fiscal_year": filters["from_fiscal_year"], - "to_fiscal_year": filters["to_fiscal_year"], - }, - ) + { + "from_fiscal_year": filters["from_fiscal_year"], + "to_fiscal_year": filters["to_fiscal_year"], + }, + ) - return fiscal_year - - -def get_accounts(name, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - accounts = frappe.db.sql( - """ - select - distinct(ba.account) - from - `tabBudget Account` ba - join - `tabBudget` b - on b.name = ba.parent - where - b.docstatus = 1 - and b.fiscal_year between %s and %s - and b.{budget_against} = %s - order by - ba.account - """.format( - budget_against=budget_against - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - ) - - return accounts - - -def get_monthly_distribution(name, year, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - monthly_distribution = frappe.db.sql( - """ - select - monthly_distribution - from - `tabBudget` - where - docstatus = 1 - and {budget_against} = %s - and fiscal_year = %s - """.format( - budget_against=budget_against - ), - (name, year), - ) - - return monthly_distribution[0][0] if monthly_distribution else None - - -def get_budget_amount(name, year, account, filters): - budget_against = frappe.scrub(filters.get("budget_against")) - - budget_amount = frappe.db.sql( - """ - select - ba.budget_amount - from - `tabBudget Account` ba - join - `tabBudget` b - on b.name = ba.parent - where - b.docstatus = 1 - and b.{budget_against} = %s - and b.fiscal_year = %s - and ba.account = %s - """.format( - budget_against=budget_against - ), - (name, year, account), - ) - - return budget_amount[0][0] if budget_amount else 0 + return fiscal_year From aadf264b497677a6d962f3086f5d7fabc6528f17 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 9 May 2020 16:15:51 +0530 Subject: [PATCH 114/410] Assessment Plan not getting created --- .../assessment_plan/assessment_plan.json | 702 ++---------------- 1 file changed, 54 insertions(+), 648 deletions(-) diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.json b/erpnext/education/doctype/assessment_plan/assessment_plan.json index bc3946440c3..95ed853c219 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan.json +++ b/erpnext/education/doctype/assessment_plan/assessment_plan.json @@ -1,790 +1,207 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, - "allow_rename": 0, "autoname": "EDU-ASP-.YYYY.-.#####", - "beta": 0, "creation": "2015-11-12 16:34:34.658092", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "student_group", + "assessment_name", + "assessment_group", + "grading_scale", + "column_break_2", + "course", + "program", + "academic_year", + "academic_term", + "section_break_5", + "schedule_date", + "room", + "examiner", + "examiner_name", + "column_break_4", + "from_time", + "to_time", + "supervisor", + "supervisor_name", + "section_break_20", + "maximum_assessment_score", + "assessment_criteria", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "student_group", "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": 1, "label": "Student Group", - "length": 0, - "no_copy": 0, "options": "Student Group", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Assessment Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Assessment Name" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_group", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Assessment Group", - "length": 0, - "no_copy": 0, "options": "Assessment Group", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "course.default_grading_scale", + "fetch_if_empty": 1, "fieldname": "grading_scale", "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": 1, "label": "Grading Scale", - "length": 0, - "no_copy": 0, "options": "Grading Scale", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.course", + "fetch_if_empty": 1, "fieldname": "course", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Course", - "length": 0, - "no_copy": 0, "options": "Course", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.program", "fieldname": "program", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Program" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.academic_year", "fieldname": "academic_year", "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": "Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Academic Year" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "student_group.academic_term", "fieldname": "academic_term", "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": "Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Academic Term" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", "fieldname": "section_break_5", "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": "Schedule", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Schedule" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "schedule_date", "fieldtype": "Date", - "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": "Schedule Date", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "room", "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": "Room", - "length": 0, - "no_copy": 0, - "options": "Room", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Room" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "examiner", "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": "Examiner", - "length": 0, - "no_copy": 0, - "options": "Instructor", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Instructor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "examiner.instructor_name", "fieldname": "examiner_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Examiner Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_time", "fieldtype": "Time", - "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": "From Time", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_time", "fieldtype": "Time", - "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": "To Time", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "supervisor", "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": "Supervisor", - "length": 0, - "no_copy": 0, - "options": "Instructor", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Instructor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "supervisor.instructor_name", "fieldname": "supervisor_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Supervisor Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_20", "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": "Evaluate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Evaluate" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "maximum_assessment_score", "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": "Maximum Assessment Score", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "assessment_criteria", "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": "Assessment Criteria", - "length": 0, - "no_copy": 0, "options": "Assessment Plan Criteria", - "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 + "reqd": 1 }, { - "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": "Assessment Plan", - "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 + "read_only": 1 } ], - "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, - "menu_index": 0, - "modified": "2018-08-30 00:48:03.475522", + "links": [], + "modified": "2020-05-09 14:56:26.746988", "modified_by": "Administrator", "module": "Education", "name": "Assessment Plan", - "name_case": "", "owner": "Administrator", "permissions": [ { @@ -794,28 +211,17 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Academics User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "title_field": "assessment_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "assessment_name" } \ No newline at end of file From 65d3ac814d053cd1c191c8345bf8226fde4d75a7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 9 May 2020 17:39:59 +0530 Subject: [PATCH 115/410] fix: Add accidentally removed function --- erpnext/accounts/dashboard_fixtures.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 30eb5e31156..a106f70dd06 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -107,6 +107,10 @@ def get_charts(): } ] +def get_account(account_type, company): + accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) + if accounts: + return accounts[0].name def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company From b81bd5f70c13e269a6a28bbd28022978d1d2a46f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 10 May 2020 17:24:11 +0530 Subject: [PATCH 116/410] fix: Formatting fixes --- .../budget_variance_report.py | 69 +++++-------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 4d5892a9132..49c1d0f2ccd 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -22,9 +22,7 @@ def execute(filters=None): else: dimensions = get_cost_centers(filters) - period_month_ranges = get_period_month_ranges( - filters["period"], filters["from_fiscal_year"] - ) + period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"]) cam_map = get_dimension_account_month_map(filters) data = [] @@ -41,9 +39,7 @@ def execute(filters=None): for month in relevant_months: if monthwise_data.get(year[0]): month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate( - ["target", "actual", "variance"] - ): + for i, fieldname in enumerate(["target", "actual", "variance"]): value = flt(month_data.get(fieldname)) period_data[i] += value totals[i] += value @@ -67,7 +63,7 @@ def get_columns(filters): columns = [ _(filters.get("budget_against")) + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150", + _("Account") + ":Link/Account:150" ] group_months = False if filters["period"] == "Monthly" else True @@ -80,7 +76,7 @@ def get_columns(filters): labels = [ _("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), - _("Variance ") + " " + str(year[0]), + _("Variance ") + " " + str(year[0]) ] for label in labels: columns.append(label + ":Float:150") @@ -88,7 +84,7 @@ def get_columns(filters): for label in [ _("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), - _("Variance") + " (%s)" + " " + str(year[0]), + _("Variance") + " (%s)" + " " + str(year[0]) ]: if group_months: label = label % ( @@ -105,7 +101,7 @@ def get_columns(filters): return columns + [ _("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150", + _("Total Variance") + ":Float:150" ] else: return columns @@ -126,11 +122,8 @@ def get_cost_centers(filters): where company = %s {order_by} - """.format( - tab=filters.get("budget_against"), order_by=order_by - ), - filters.get("company"), - ) + """.format(tab=filters.get("budget_against"), order_by=order_by), + filters.get("company")) else: return frappe.db.sql_list( """ @@ -138,10 +131,7 @@ def get_cost_centers(filters): name from `tab{tab}` - """.format( - tab=filters.get("budget_against") - ) - ) # nosec + """.format(tab=filters.get("budget_against"))) # nosec # Get dimension & target details @@ -149,14 +139,8 @@ def get_dimension_target_details(filters): budget_against = frappe.scrub(filters.get("budget_against")) cond = "" if filters.get("budget_against_filter"): - cond += """ - and - b.{budget_against} in ( - %s - ) - """.format( - budget_against=budget_against - ) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) + cond += """ and b.{budget_against} in (%s)""".format( + budget_against=budget_against) % ", ".join(["%s"] * len(filters.get("budget_against_filter"))) return frappe.db.sql( """ @@ -190,9 +174,7 @@ def get_dimension_target_details(filters): filters.company, ] + filters.get("budget_against_filter") - ), - as_dict=True, - ) + ), as_dict=True) # Get target distribution details of accounts of cost center @@ -213,29 +195,24 @@ def get_target_distribution_details(filters): order by md.fiscal_year """, - (filters.from_fiscal_year, filters.to_fiscal_year), - as_dict=1, - ): + (filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1): target_details.setdefault(d.name, {}).setdefault( d.month, flt(d.percentage_allocation) ) return target_details - # Get actual details from gl entry def get_actual_details(name, filters): budget_against = frappe.scrub(filters.get("budget_against")) - cond = "" + if filters.get("budget_against") == "Cost Center": cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cond = """ and lft >= "{lft}" and rgt <= "{rgt}" - """.format( - lft=cc_lft, rgt=cc_rgt - ) + """.format(lft=cc_lft, rgt=cc_rgt) ac_details = frappe.db.sql( """ @@ -269,12 +246,8 @@ def get_actual_details(name, filters): group by gl.name order by gl.fiscal_year - """.format( - tab=filters.budget_against, budget_against=budget_against, cond=cond - ), - (filters.from_fiscal_year, filters.to_fiscal_year, name), - as_dict=1, - ) + """.format(tab=filters.budget_against, budget_against=budget_against, cond=cond), + (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1) cc_actual_details = {} for d in ac_details: @@ -282,7 +255,6 @@ def get_actual_details(name, filters): return cc_actual_details - def get_dimension_account_month_map(filters): dimension_target_details = get_dimension_target_details(filters) tdd = get_target_distribution_details(filters) @@ -326,13 +298,10 @@ def get_fiscal_years(filters): `tabFiscal Year` where name between %(from_fiscal_year)s and %(to_fiscal_year)s - order by - year """, { "from_fiscal_year": filters["from_fiscal_year"], - "to_fiscal_year": filters["to_fiscal_year"], - }, - ) + "to_fiscal_year": filters["to_fiscal_year"] + }) return fiscal_year From 3a914a7cd74abf5445e1d9d45b84c3b307229911 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 11 May 2020 11:44:45 +0530 Subject: [PATCH 117/410] fix: '<' not supported between instances of 'str' and 'int' --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index ddf4ec03937..10f25554757 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -181,7 +181,7 @@ class StockEntry(StockController): stock_items = self.get_stock_items() serialized_items = self.get_serialized_items() for item in self.get("items"): - if item.qty and item.qty < 0: + if flt(item.qty) and flt(item.qty) < 0: frappe.throw(_("Row {0}: The item {1}, quantity must be positive number") .format(item.idx, frappe.bold(item.item_code))) From 0defefda92ee28158ceb54d8cd1b639e76df94fc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 11 May 2020 12:14:46 +0530 Subject: [PATCH 118/410] feat: Standard accounts dashboard --- erpnext/accounts/accounts | 0 .../account_balance_timeline.py | 2 +- erpnext/accounts/dashboard_fixtures.py | 153 ++++++++++++++++++ .../accounts_receivable.py | 2 +- .../setup_wizard/data/dashboard_charts.py | 97 ----------- 5 files changed, 155 insertions(+), 99 deletions(-) create mode 100644 erpnext/accounts/accounts create mode 100644 erpnext/accounts/dashboard_fixtures.py diff --git a/erpnext/accounts/accounts b/erpnext/accounts/accounts new file mode 100644 index 00000000000..e69de29bb2d 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 c3e2f7db124..5decccb4869 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 @@ -6,7 +6,7 @@ import frappe, json from frappe import _ 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.utils.dashboard import cache_source, get_from_date_from_timespan from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending from frappe.utils.nestedset import get_descendants_of diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py new file mode 100644 index 00000000000..746eb649437 --- /dev/null +++ b/erpnext/accounts/dashboard_fixtures.py @@ -0,0 +1,153 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe.utils import nowdate, add_months + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts() + }) + +def get_dashboards(): + + return [{ + "name": "Accounts Dashboard", + "dashboard_name": "Accounts", + "doctype": "Dashboard", + "charts": [ + { "chart": "Profit and Loss" , "width": "Full"}, + { "chart": "Incoming Bills"}, + { "chart": "Outgoing Bills"}, + { "chart": "Accounts Receivable Ageing"}, + { "chart": "Accounts Payable Ageing"}, + { "chart": "Bank Balance", "width": "Full"}, + ] + }] + +def get_charts(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return [ + { + "doctype": "Dashboard Charts", + "name": "Profit and Loss", + "owner": "Administrator", + "report_name": "Profit and Loss Statement", + "filters_json": json.dumps({ + "company": company.name, + "filter_based_on": "Date Range", + "period_start_date": add_months(nowdate(), -4), + "period_end_date": nowdate(), + "periodicity": "Monthly", + "include_default_book_entries": 1 + }), + "type": "Bar", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Profit and Loss", + "is_custom": 1 + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Incoming Bills", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "name": "Outgoing Bills", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Charts", + "name": "Accounts Receivable Ageing", + "owner": "Administrator", + "report_name": "Accounts Receivable", + "filters_json": json.dumps({ + "company": company.name, + "report_date": nowdate(), + "ageing_based_on": "Due Date", + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120 + }), + "type": "Donut", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Accounts Receivable Ageing", + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Accounts Payable Ageing", + "owner": "Administrator", + "report_name": "Accounts Payable", + "filters_json": json.dumps({ + "company": company.name, + "report_date": nowdate(), + "ageing_based_on": "Due Date", + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120 + }), + "type": "Donut", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Accounts Payable Ageing", + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Bank Balance", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + + ] \ No newline at end of file diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e9c286fcf0d..a0a1b9783ac 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -546,7 +546,7 @@ class ReceivablePayableReport(object): self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = 30, 60, 90, 120 for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]): - if row.age <= days: + if cint(row.age) <= cint(days): index = i break diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index b182dfc103f..2828307204d 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -3,21 +3,8 @@ from frappe import _ import frappe import json -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None def get_default_dashboards(): - company = frappe.get_doc("Company", get_company_for_dashboards()) - income_account = company.default_income_account or get_account("Income Account", company.name) - expense_account = company.default_expense_account or get_account("Expense Account", company.name) - bank_account = company.default_bank_account or get_account("Bank", company.name) return { "Dashboards": [ @@ -25,90 +12,11 @@ def get_default_dashboards(): "doctype": "Dashboard", "dashboard_name": "Accounts", "charts": [ - { "chart": "Outgoing Bills (Sales Invoice)" }, - { "chart": "Incoming Bills (Purchase Invoice)" }, - { "chart": "Bank Balance" }, - { "chart": "Income" }, - { "chart": "Expenses" }, { "chart": "Patient Appointments" } ] } ], "Charts": [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half" - }, { "doctype": "Dashboard Chart", "time_interval": "Daily", @@ -126,8 +34,3 @@ def get_default_dashboards(): } ] } - -def get_account(account_type, company): - accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) - if accounts: - return accounts[0].name From 6e5952fa6952c3cae1a860c8825f4ed13e65dac1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 11 May 2020 16:11:43 +0530 Subject: [PATCH 119/410] fix: Changed label for payroll working days based on field --- erpnext/hr/doctype/hr_settings/hr_settings.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 9161ed822ac..ebf8723be65 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -13,12 +13,12 @@ "stop_birthday_reminders", "expense_approver_mandatory_in_expense_claim", "payroll_settings", - "payroll_based_on", - "max_working_hours_against_timesheet", + "payroll_based_on", + "max_working_hours_against_timesheet", "include_holidays_in_total_working_days", "disable_rounded_total", "column_break_11", - "daily_wages_fraction_for_half_day", + "daily_wages_fraction_for_half_day", "email_salary_slip_to_employee", "encrypt_salary_slips_in_emails", "password_policy", @@ -191,7 +191,7 @@ "default": "Leave", "fieldname": "payroll_based_on", "fieldtype": "Select", - "label": "Calculate Working Days in Payroll based on", + "label": "Calculate Payroll Working Days Based On", "options": "Leave\nAttendance" }, { @@ -206,7 +206,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2020-04-13 21:20:59.382394", + "modified": "2020-05-11 13:02:51.274347", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", From 85a89812a42a8d8e5f80dbec89c77e5fe5e82988 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 11 May 2020 19:23:18 +0530 Subject: [PATCH 120/410] fix: Run income-tax-slab patch only if slab already exists in payroll period (#21684) --- ...ve_tax_slabs_from_payroll_period_to_income_tax_slab.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index 179be2cfde5..ec94cd01d1e 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -7,7 +7,7 @@ import frappe from frappe.model.utils.rename_field import rename_field def execute(): - if not frappe.db.table_exists("Payroll Period"): + if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")): return for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): @@ -60,6 +60,9 @@ def execute(): """, (income_tax_slab.name, company.name, period.start_date)) # move other incomes to separate document + if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"): + return + migrated = [] proofs = frappe.get_all("Employee Tax Exemption Proof Submission", filters = {'docstatus': 1}, @@ -79,6 +82,9 @@ def execute(): except: pass + if not frappe.db.table_exists("Employee Tax Exemption Declaration"): + return + declerations = frappe.get_all("Employee Tax Exemption Declaration", filters = {'docstatus': 1}, fields =['payroll_period', 'employee', 'company', 'income_from_other_sources'] From 28c9468dad126985ee062d9d155449124196e1e7 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Mon, 11 May 2020 19:28:10 +0530 Subject: [PATCH 121/410] fix(patch): use translated string while setting notification template (#21679) --- erpnext/patches/v11_0/set_default_email_template_in_hr.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py index 14954fbeb31..46223761095 100644 --- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py +++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +from frappe import _ import frappe def execute(): hr_settings = frappe.get_single("HR Settings") - hr_settings.leave_approval_notification_template = "Leave Approval Notification" - hr_settings.leave_status_notification_template = "Leave Status Notification" - hr_settings.save() \ No newline at end of file + hr_settings.leave_approval_notification_template = _("Leave Approval Notification") + hr_settings.leave_status_notification_template = _("Leave Status Notification") + hr_settings.save() From 30f26b4457e0633266c33ab19d0ab0096db89490 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Mon, 11 May 2020 19:54:46 +0530 Subject: [PATCH 122/410] fix: do not setup charts if not setup_complete or no company found (#21670) * fix: do not setup charts if not setup_complete or no company found Signed-off-by: Chinmay D. Pai * chore: remove check for setup_complete moved the check for setup_complete to frappe Signed-off-by: Chinmay D. Pai * chore: return an empty dict from get_data if company not set Signed-off-by: Chinmay D. Pai --- erpnext/accounts/dashboard_fixtures.py | 176 +++++++++++++------------ 1 file changed, 90 insertions(+), 86 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index a106f70dd06..cdd166134f9 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -1,15 +1,22 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt +from erpnext import get_default_company import frappe import json def get_data(): - return frappe._dict({ - "dashboards": get_dashboards(), - "charts": get_charts(), + data = frappe._dict({ + "dashboards": [], + "charts": [] }) + company = get_company_for_dashboards() + if company: + company_doc = frappe.get_doc("Company", company) + data.dashboards = get_dashboards() + data.charts = get_charts(company_doc) + return data def get_dashboards(): return [{ @@ -24,88 +31,87 @@ def get_dashboards(): ] }] -def get_charts(): - company = frappe.get_doc("Company", get_company_for_dashboards()) +def get_charts(company): income_account = company.default_income_account or get_account("Income Account", company.name) expense_account = company.default_expense_account or get_account("Expense Account", company.name) bank_account = company.default_bank_account or get_account("Bank", company.name) return [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "name": "Income", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "name": "Expenses", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "name": "Bank Balance", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "name": "Incoming Bills (Purchase Invoice)", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "name": "Outgoing Bills (Sales Invoice)", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar" - } - ] + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Income", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Expenses", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "name": "Bank Balance", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Incoming Bills (Purchase Invoice)", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Outgoing Bills (Sales Invoice)", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar" + } + ] def get_account(account_type, company): accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) @@ -113,11 +119,9 @@ def get_account(account_type, company): return accounts[0].name def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: + company = get_default_company() + if not company: company_list = frappe.get_list("Company") if company_list: - return company_list[0].name - return None \ No newline at end of file + company = company_list[0].name + return company From b52e40037abe4453934cc8673b3aa36bb35aaf59 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 11 May 2020 20:01:57 +0530 Subject: [PATCH 123/410] fix: Remove domain restriction from Location doctype (#21659) --- erpnext/assets/doctype/location/location.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 6a35130f30a..f56fd05d98c 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -141,7 +141,7 @@ ], "is_tree": 1, "links": [], - "modified": "2020-03-18 18:00:08.885805", + "modified": "2020-05-08 16:11:11.375701", "modified_by": "Administrator", "module": "Assets", "name": "Location", @@ -221,7 +221,6 @@ } ], "quick_entry": 1, - "restrict_to_domain": "Agriculture", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", From 97715f2877320486353a17662ea5f57f8eaf4f8c Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 11 May 2020 20:45:37 +0530 Subject: [PATCH 124/410] fix: Message for missing valuation rate (#21686) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- erpnext/stock/stock_ledger.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 10f25554757..62c9eb1eb2c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -470,7 +470,7 @@ class StockEntry(StockController): "qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty), "serial_no": item.serial_no, "voucher_type": self.doctype, - "voucher_no": item.name, + "voucher_no": self.name, "company": self.company, "allow_zero_valuation": item.allow_zero_valuation_rate, }) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b4cb8cadb45..e1b3730f2f9 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -548,7 +548,16 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \ and cint(erpnext.is_perpetual_inventory_enabled(company)): frappe.local.message_log = [] - frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.") - .format(item_code, voucher_type, voucher_no)) + form_link = frappe.utils.get_link_to_form("Item", item_code) + + message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no) + message += "

" + _(" Here are the options to proceed:") + solutions = "
  • " + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "
  • " + solutions += "
  • " + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "
  • " + sub_solutions = "
    • " + _("Create an incoming stock transaction for the Item.") + "
    • " + sub_solutions += "
    • " + _("Mention Valuation Rate in the Item master.") + "
    " + msg = message + solutions + sub_solutions + "" + + frappe.throw(msg=msg, title=_("Valuation Rate Missing")) return valuation_rate From 498f6583210e730207e922823c5e6d87579c55b8 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 11 May 2020 20:47:08 +0530 Subject: [PATCH 125/410] refactor: rename getting started to home (#21674) * feat: rename getting started to home * feat: added patch for renaming --- erpnext/patches.txt | 1 + .../getting_started.json => home/home.json} | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) rename erpnext/setup/desk_page/{getting_started/getting_started.json => home/home.json} (97%) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3f90d36916e..f72172474cb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") +execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) diff --git a/erpnext/setup/desk_page/getting_started/getting_started.json b/erpnext/setup/desk_page/home/home.json similarity index 97% rename from erpnext/setup/desk_page/getting_started/getting_started.json rename to erpnext/setup/desk_page/home/home.json index 63d8984c405..63cd5c5ceca 100644 --- a/erpnext/setup/desk_page/getting_started/getting_started.json +++ b/erpnext/setup/desk_page/home/home.json @@ -47,26 +47,20 @@ } ], "category": "Modules", - "charts": [ - { - "chart_name": "Bank Balance", - "label": "Bank Balance" - } - ], + "charts": [], "creation": "2020-01-23 13:46:38.833076", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, - "label": "Getting Started", - "modified": "2020-04-01 11:30:19.763099", + "label": "Home", + "modified": "2020-05-11 10:20:37.358701", "modified_by": "Administrator", "module": "Setup", - "name": "Getting Started", + "name": "Home", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 1, From d99f85bf3c514cd018dd7a0f3d851598a265026b Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 13:16:05 +0530 Subject: [PATCH 126/410] chore: Fixtures using new sync api --- erpnext/buying/dashboard_fixtures.py | 28 +++++++ .../setup_wizard/data/dashboard_charts.py | 81 ------------------- 2 files changed, 28 insertions(+), 81 deletions(-) create mode 100644 erpnext/buying/dashboard_fixtures.py diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py new file mode 100644 index 00000000000..199765bacec --- /dev/null +++ b/erpnext/buying/dashboard_fixtures.py @@ -0,0 +1,28 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "", + "dashboard_name": "", + "charts": [ + { "chart": } + ] + }] + +def get_charts(): + return [ { ... } ] + +def get_number_cards(): + return [ { ... } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 9c654a829d5..88d42590fc9 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -134,87 +134,6 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "type": "Line", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Purchase Order Analysis", - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 - }), - "is_custom": 1, - "type": "Bar", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Requested Items to Order", - "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) } ] } From 734bfcdfc2878ea9e69d90f83a02a30912cecb26 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 May 2020 14:11:33 +0530 Subject: [PATCH 127/410] fix: purchase inv shows overdue for fraction of outstanding --- .../purchase_invoice/purchase_invoice.py | 34 +++++++++++++++++++ erpnext/controllers/status_updater.py | 11 ------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3aa24df16d3..5b16eb4640b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1021,6 +1021,40 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() + + 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() + + if not status: + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + elif outstanding_amount > 0 and due_date < nowdate: + self.status = "Overdue" + elif outstanding_amount > 0 and due_date >= nowdate: + self.status = "Unpaid" + #Check if outstanding amount is 0 due to debit 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 = "Debit 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) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index de76e45cd11..b465a106f0e 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -69,17 +69,6 @@ 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'"], From cdeb897fff6613bc0dd103638e0f7fdb3a4f82bd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 12 May 2020 17:23:15 +0530 Subject: [PATCH 128/410] fix: Account shortcut in desk page --- .../desk_page/accounting/accounting.json | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 0d6aca65b16..648ebe81a15 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -45,11 +45,6 @@ "label": "Bank Statement", "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Clearance Dates\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]", - "title": "Banking and Payments" - }, { "hidden": 0, "label": "Subscription Management", @@ -99,11 +94,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-04-29 12:17:34.844397", + "modified": "2020-05-12 17:20:27.573449", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -112,10 +106,20 @@ "pin_to_top": 0, "shortcuts": [ { - "label": "Account", + "label": "Chart Of Accounts", "link_to": "Account", "type": "DocType" }, + { + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "type": "DocType" + }, + { + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "type": "DocType" + }, { "label": "Journal Entry", "link_to": "Journal Entry", From 354b01617effcfae1cf2cd03252f4a60a5814c99 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 17:35:17 +0530 Subject: [PATCH 129/410] chore: Dashboard Fixtures --- erpnext/buying/dashboard_fixtures.py | 112 +++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 199765bacec..0bd9a1fd488 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -12,17 +12,119 @@ def get_data(): "number_cards": get_number_cards(), }) +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) + def get_dashboards(): return [{ - "name": "", - "dashboard_name": "", + "name": "Buying", + "dashboard_name": "Buying", "charts": [ - { "chart": } + { "chart": "Purchase Analytics", "width": "Full"}, + { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Purchase Order Analysis", "width": "Half"}, + { "chart": "Requested Items to Order", "width": "Full"} ] }] def get_charts(): - return [ { ... } ] + return [ + { + "name": "Purchase Analytics", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "x_field": "entity", + "type": "Bar", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Bar", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"] + }) + }, + { + "name": "Material Request Purchase Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "name": "Purchase Order Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Requested Items to Order", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] def get_number_cards(): - return [ { ... } ] \ No newline at end of file + return [{}] \ No newline at end of file From 7adca986fbecb95ad4c209838dd0042092cb78ce Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:40:35 +0530 Subject: [PATCH 130/410] chore: Onboarding v2 --- erpnext/buying/onboarding/buying/buying.json | 11 ++++++++++- .../buying_settings/buying_settings.json | 2 +- .../create_a_product/create_a_product.json | 16 ++++++++++++++++ .../create_a_supplier/create_a_supplier.json | 4 ++-- .../create_a_warehouse/create_a_warehouse.json | 16 ++++++++++++++++ .../create_your_first_purchase_order.json | 16 ++++++++++++++++ .../introduction_to_buying.json | 2 +- 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 erpnext/buying/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json create mode 100644 erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json index c35309964e7..1ee98877bd8 100644 --- a/erpnext/buying/onboarding/buying/buying.json +++ b/erpnext/buying/onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-06 16:49:17.433261", + "modified": "2020-05-12 18:32:05.085967", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -31,8 +31,17 @@ { "step": "Create a Supplier" }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, { "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" } ], "subtitle": "Products, Purchases, Analysis and more.", diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 7274c0d62d1..3b3208f5f04 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:51:56.862206", + "modified": "2020-05-12 18:30:06.323797", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..dce1a215053 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Product" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index 14164c33037..f87a43dbc39 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -5,9 +5,9 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, + "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 17:00:41.446942", + "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json new file mode 100644 index 00000000000..20744f6624b --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:00:03.027704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:27:44.128737", + "modified_by": "Administrator", + "name": "Create a Warehouse", + "owner": "Administrator", + "reference_document": "Warehouse", + "title": "Setup your Purchase Warehouse" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json new file mode 100644 index 00000000000..0efadbd7887 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:17:49.976035", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:31:56.856112", + "modified_by": "Administrator", + "name": "Create your first Purchase Order", + "owner": "Administrator", + "reference_document": "Purchase Order", + "title": "Create your first Purchase Order" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index f1a027af369..73f22df48c1 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:47:47.378049", + "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", From e4fc5ba83cd3360e58d779b91d15f77c14a489ca Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:58:54 +0530 Subject: [PATCH 131/410] fix: Report conditional query simplification. --- .../purchase_order_analysis.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 78b86636e07..96e2fc8a209 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -36,25 +36,20 @@ def validate_filters(filters): def get_conditions(filters): conditions = "" if filters.get("from_date") and filters.get("to_date"): - conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" if filters.get("company"): - conditions += " and po.company = '{0}'".format(filters.get("company")) + conditions += " and po.company = %(company)s" if filters.get("purchase_order"): - conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + conditions += " and po.name = %(purchase_order)s" if filters.get("status"): - conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + conditions += " and po.status in %(status)s" return conditions def get_data(conditions, filters): - status = filters.get("status") - # temporary fix for dashboard chart - if status is None: - status = [] - data = frappe.db.sql(""" SELECT po.transaction_date as date, @@ -82,7 +77,7 @@ def get_data(conditions, filters): {0} GROUP BY poi.name ORDER BY po.transaction_date ASC - """.format(conditions), tuple(status), as_dict=1) + """.format(conditions), filters, as_dict=1) return data From 673e704bb5daefbc3c128bafee2f7051eb60d7cb Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 12 May 2020 19:09:27 +0530 Subject: [PATCH 132/410] typo in error message in loan_security_pledge.py --- .../doctype/loan_security_pledge/loan_security_pledge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py index f97e5965a5f..961c05c9c1d 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py @@ -38,7 +38,7 @@ class LoanSecurityPledge(Document): for pledge in self.securities: if not pledge.qty and not pledge.amount: - frappe.throw(_("Qty or Amount is mandatroy for loan security")) + frappe.throw(_("Qty or Amount is mandatory for loan security!")) if not (self.loan_application and pledge.loan_security_price): pledge.loan_security_price = get_loan_security_price(pledge.loan_security) From 36aea71fd76d04641ee18b0a363ad182da7ad89a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 10:47:36 +0530 Subject: [PATCH 133/410] fix: new dashboard and report --- erpnext/hr/dashboard_fixtures.py | 154 ++++++++++++++++++ erpnext/hr/doctype/attendance/attendance.py | 6 +- .../leave_application/leave_application.py | 4 +- .../department_analytics.json | 28 ---- .../__init__.py | 0 .../employee_analytics.js} | 12 +- .../employee_analytics.json | 30 ++++ .../employee_analytics.py} | 60 ++++--- .../monthly_attendance_sheet.py | 101 +++++++++--- .../production_analytics.py | 1 - erpnext/patches.txt | 1 + 11 files changed, 314 insertions(+), 83 deletions(-) create mode 100644 erpnext/hr/dashboard_fixtures.py delete mode 100644 erpnext/hr/report/department_analytics/department_analytics.json rename erpnext/hr/report/{department_analytics => employee_analytics}/__init__.py (100%) rename erpnext/hr/report/{department_analytics/department_analytics.js => employee_analytics/employee_analytics.js} (55%) create mode 100644 erpnext/hr/report/employee_analytics/employee_analytics.json rename erpnext/hr/report/{department_analytics/department_analytics.py => employee_analytics/employee_analytics.py} (51%) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py new file mode 100644 index 00000000000..dafacaada41 --- /dev/null +++ b/erpnext/hr/dashboard_fixtures.py @@ -0,0 +1,154 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import erpnext +import json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + dashboards = [] + dashboards.append(get_human_resource_dashboard()) + return dashboards + +def get_human_resource_dashboard(): + return { + "name": "Human Resource", + "dashboard_name": "Human Resource", + "is_default": 1, + "charts": [ + { "chart": "Gender Diversity Ratio", "width": "Half"}, + { "chart": "Employee Count", "width": "Half"}, + { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Attendance Count", "width": "Full"} + ], + "cards": [ + {"card": "Total Employees"}, + {"card": "New Joinees"}, + {'card': "Job Applicants"}, + {'card': "Employees Left"} + ] + } + +def get_recruitment_dashboard(): + pass + # return { + # "name": "Human Resource", + # "dashboard_name": "Human Resource", + # "is_default": 1, + # "charts": [ + # ], + # "cards": [ + # ] + # } + + +def get_charts(): + company = erpnext.get_default_company() + + if not company: + company = frappe.db.get_value("Company", {"is_group": 0}, "name") + + dashboard_charts = [ + get_dashboards_chart_doc('Gender Diversity Ratio', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="gender", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ] + + dashboard_charts.append( + get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + ) + + custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' + filters_json = json.dumps({"month":"May","year":"2020","company":company}) + + dashboard_charts.append( + get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" + filters_json = json.dumps({"company":company ,"parameter":"Department"}) + + dashboard_charts.append( + get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + + + +def get_number_cards(): + number_cards = [] + + number_cards = [ + get_number_cards_doc("Employee", "Total Employees", filters_json = json.dumps([ + ["Employee","status","=","Active"] + ]) + ) + ] + number_cards.append( + get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ + ["Employee","date_of_joining","Previous","6 months"], + ["Employee","status","=","Active"] + ]), + stats_time_interval = "Daily") + ) + number_cards.append( + get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","status","=","Left"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ + ["Job Applicant","status","!=","Rejected"] + ]) + ) + ) + + return number_cards + + +def get_number_cards_doc(document_type, label, **args): + args = frappe._dict(args) + + return { + "doctype": "Number Card", + "document_type": document_type, + "function": args.func or "Count", + "is_public": args.is_public or 1, + "label": label, + "name": args.name or label, + "show_percentage_stats": args.show_percentage_stats or 1, + "stats_time_interval": args.stats_time_interval or 'Monthly', + "filters_json": args.filters_json or '[]', + } + +def get_dashboards_chart_doc(name, chart_type, graph_type, **args): + + args = frappe._dict(args) + + return { + "name": name, + "chart_name": args.chart_name or name, + "chart_type": chart_type, + "document_type": args.document_type or None, + "report_name": args.report_name or None, + "is_custom": args.is_custom or 0, + "group_by_type": args.group_by_type or None, + "group_by_based_on": args.group_by_based_on or None, + "based_on": args.based_on or None, + "value_based_on": args.value_based_on or None, + "number_of_groups": args.number_of_groups or 0, + "is_public": args.is_public or 1, + "timespan": args.timespan or "Last Year", + "time_interval": args.time_interval or "Yearly", + "timeseries": args.timeseries or 0, + "filters_json": args.filters_json or '[]', + "type": graph_type, + "custom_options": args.custom_options or '', + "doctype": "Dashboard Chart", + } \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b6c80655c2e..ba804ceca57 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -41,7 +41,7 @@ class Attendance(Document): leave_record = frappe.db.sql(""" select leave_type, half_day, half_day_date from `tabLeave Application` - where employee = %s + where employee = %s and %s between from_date and to_date and status = 'Approved' and docstatus = 1 @@ -172,8 +172,8 @@ def get_unmarked_days(employee, month): records = frappe.get_all("Attendance", fields = ['attendance_date', 'employee'] , filters = [ - ["attendance_date", ">", month_start], - ["attendance_date", "<", month_end], + ["attendance_date", ">=", month_start], + ["attendance_date", "<=", month_end], ["employee", "=", employee], ["docstatus", "!=", 2] ]) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 47b1bb7684e..d3a08cd3c73 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -131,6 +131,8 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" + print("-------->>>", status) + # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -596,7 +598,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date): is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 + AND docstatus=1 AND (leaves<0 OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s diff --git a/erpnext/hr/report/department_analytics/department_analytics.json b/erpnext/hr/report/department_analytics/department_analytics.json deleted file mode 100644 index 1e26b33c530..00000000000 --- a/erpnext/hr/report/department_analytics/department_analytics.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2018-05-15 15:37:20.883263", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-05-15 17:19:32.934321", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Analytics", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Department Analytics", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/__init__.py b/erpnext/hr/report/employee_analytics/__init__.py similarity index 100% rename from erpnext/hr/report/department_analytics/__init__.py rename to erpnext/hr/report/employee_analytics/__init__.py diff --git a/erpnext/hr/report/department_analytics/department_analytics.js b/erpnext/hr/report/employee_analytics/employee_analytics.js similarity index 55% rename from erpnext/hr/report/department_analytics/department_analytics.js rename to erpnext/hr/report/employee_analytics/employee_analytics.js index 29fedcd7350..8620a65a909 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.js +++ b/erpnext/hr/report/employee_analytics/employee_analytics.js @@ -1,7 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +/* eslint-disable */ -frappe.query_reports["Department Analytics"] = { +frappe.query_reports["Employee Analytics"] = { "filters": [ { "fieldname":"company", @@ -11,5 +12,12 @@ frappe.query_reports["Department Analytics"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"parameter", + "label": __("Parameter"), + "fieldtype": "Select", + "options": ["Branch","Grade","Department","Designation", "Employment Type"], + "reqd": 1 + } ] -}; \ No newline at end of file +}; diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.json b/erpnext/hr/report/employee_analytics/employee_analytics.json new file mode 100644 index 00000000000..5a7ab9a251e --- /dev/null +++ b/erpnext/hr/report/employee_analytics/employee_analytics.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-12 13:52:50.631086", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-12 13:52:50.631086", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Employee", + "report_name": "Employee Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/department_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py similarity index 51% rename from erpnext/hr/report/department_analytics/department_analytics.py rename to erpnext/hr/report/employee_analytics/employee_analytics.py index b28eac43f8b..6f8f161c3db 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -13,12 +13,13 @@ def execute(filters=None): columns = get_columns() employees = get_employees(filters) - departments_result = get_department(filters) - departments = [] - if departments_result: - for department in departments_result: - departments.append(department) - chart = get_chart_data(departments,employees) + parameters_result = get_parameters(filters) + parameters = [] + if parameters_result: + for department in parameters_result: + parameters.append(department) + + chart = get_chart_data(parameters,employees, filters) return columns, employees, None, chart def get_columns(): @@ -29,10 +30,8 @@ def get_columns(): ] def get_conditions(filters): - conditions = "" - if filters.get("department"): conditions += " and department = '%s'" % \ - filters["department"].replace("'", "\\'") - + conditions = " and "+filters.get("parameter").lower().replace(" ","_")+" IS NOT NULL " + if filters.get("company"): conditions += " and company = '%s'" % \ filters["company"].replace("'", "\\'") return conditions @@ -43,25 +42,38 @@ def get_employees(filters): branch, department, designation, gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) -def get_department(filters): - return frappe.db.sql("""select name from `tabDepartment` where company = %s""", (filters["company"]), as_list=1) - -def get_chart_data(departments,employees): - if not departments: - departments = [] +def get_parameters(filters): + return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + +def get_chart_data(parameters,employees, filters): + if not parameters: + parameters = [] datasets = [] - for department in departments: - if department: - total_employee = frappe.db.sql("""select count(*) from \ - `tabEmployee` where \ - department = %s""" ,(department[0]), as_list=1) + parameter_field_name = filters.get("parameter").lower().replace(" ","_") + label = [] + for parameter in parameters: + if parameter: + total_employee = frappe.db.sql("""select count(*) from + `tabEmployee` where """+ + parameter_field_name + """ = %s and company = %s""" ,( parameter[0], filters.get("company")), as_list=1) + if total_employee[0][0]: + label.append(parameter) datasets.append(total_employee[0][0]) + + values = [ value for value in datasets if value !=0] + + total_employee = frappe.db.count('Employee', {'status':'Active'}) + others = total_employee - sum(values) + + label.append(["Others"]) + values.append(others) + chart = { "data": { - 'labels': departments, - 'datasets': [{'name': 'Employees','values': datasets}] + 'labels': label, + 'datasets': [{'name': 'Employees','values': values}] } } - chart["type"] = "bar" + chart["type"] = "donut" return chart 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 82ed27715f7..a6509766c7c 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -31,7 +31,7 @@ def execute(filters=None): if not filters: filters = {} conditions, filters = get_conditions(filters) - columns = get_columns(filters) + columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) if filters.group_by: @@ -60,20 +60,77 @@ def execute(filters=None): columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"]) if filters.group_by: + emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + emp_att_map.update(aaa) data += record else: - record = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) data += record - return columns, data + chart_data = get_chart_data(emp_att_map, days) + + return columns, data, None, chart_data + +def get_chart_data(emp_att_map, days): + from pprint import pprint + pprint(emp_att_map) + labels = [] + datasets = [ + {"name": "Absent", "values": []}, + {"name": "Present", "values": []}, + {"name": "Leave", "values": []}, + {"name": "Unmarked", "values": []} + ] + for idx, day in enumerate(days, start=0): + p = day.replace("::65", "") + labels.append(day.replace("::65", "")) + total_absent_on_day = 0 + total_leave_on_day = 0 + total_present_on_day = 0 + total_holiday = 0 + total_unmarked_on_day = 0 + for emp in emp_att_map.keys(): + if emp_att_map[emp][idx]: + if emp_att_map[emp][idx] == "A": + total_absent_on_day += 1 + if emp_att_map[emp][idx] in ["P", "WFH"]: + total_present_on_day += 1 + if emp_att_map[emp][idx] == "HD": + total_present_on_day += 0.5 + total_leave_on_day += 0.5 + if emp_att_map[emp][idx] == "L": + total_leave_on_day += 1 + + total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day + + datasets[0]["values"].append(total_absent_on_day) + datasets[1]["values"].append(total_present_on_day) + datasets[2]["values"].append(total_leave_on_day) + datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) -def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None): + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + } + } + + chart["type"] = "bar" + # chart["spaceRatio"] = 0.1 + + + return chart + + + +def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): record = [] + emp_att_map = {} for emp in employee_map: emp_det = employee_map.get(emp) if not emp_det or emp not in att_map: @@ -85,6 +142,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 + ggg = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -101,19 +159,12 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list status = "Holiday" total_h += 1 - - # if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list][0]: - # if holiday_map[emp_holiday_list][1]: - # status= "Weekly Off" - # else: - # status = "Holiday" - - # += 1 + ggg.append(status_map.get(status, "")) if not filters.summarized_view: - row.append(status_map.get(status, "")) + row += ggg else: - if status == "Present": + if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": total_a += 1 @@ -159,10 +210,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) + emp_att_map[emp] = ggg record.append(row) - - return record + return record, emp_att_map def get_columns(filters): @@ -174,15 +225,17 @@ def get_columns(filters): columns += [ _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" ] - + days = [] + for day in range(filters["total_days_in_month"]): + date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) + day_name = day_abbr[getdate(date).weekday()] + days.append(cstr(day+1)+ " " +day_name +"::65") if not filters.summarized_view: - for day in range(filters["total_days_in_month"]): - date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) - day_name = day_abbr[getdate(date).weekday()] - columns.append(cstr(day+1)+ " " +day_name +"::65") - else: + columns += days + + if filters.summarized_view: columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120", _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"] - return columns + return columns, days def get_attendance_list(conditions, filters): attendance_list = frappe.db.sql("""select employee, day(attendance_date) as day_of_month, diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 7447a1f6705..145e4a79b51 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -61,7 +61,6 @@ def get_periodic_data(filters, entry): elif getdate(d.planned_start_date) < getdate(from_date) : periodic_data = update_periodic_data(periodic_data, "Overdue", period) - else: periodic_data = update_periodic_data(periodic_data, "Not Started", period) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3f90d36916e..accfa69743f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") +execute:frappe.delete_doc("Report", "Department Analytics") From 4a9fd9ef6d5e34eb6f04deb0423c93f33f0b3028 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 13 May 2020 16:11:22 +0530 Subject: [PATCH 134/410] fix: error log title for failing bank transactions --- .../doctype/plaid_settings/plaid_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index b4a5bd11a04..a7062239c32 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -209,7 +209,7 @@ def new_bank_transaction(transaction): result.append(new_transaction.name) except Exception: - frappe.throw(frappe.get_traceback()) + frappe.throw(title=_('Bank transaction creation error')) return result From 1aaedd68b9d7213804a6e244d4783674cc8865d2 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Wed, 13 May 2020 17:48:11 +0530 Subject: [PATCH 135/410] Twitter and LinkedIn Auth fix --- .../linkedin_settings/linkedin_settings.py | 4 +-- .../twitter_settings/twitter_settings.json | 34 +++++++++---------- .../twitter_settings/twitter_settings.py | 14 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py index bdde9eed379..377e061fdf4 100644 --- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py +++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py @@ -15,7 +15,7 @@ class LinkedInSettings(Document): params = urlencode({ "response_type":"code", "client_id": self.consumer_key, - "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?", + "redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()), "scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social" }) @@ -30,7 +30,7 @@ class LinkedInSettings(Document): "code": code, "client_id": self.consumer_key, "client_secret": self.get_password(fieldname="consumer_secret"), - "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?", + "redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()), } headers = { "Content-Type": "application/x-www-form-urlencoded" diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json index f92e7f04955..36776e5c202 100644 --- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json +++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.json @@ -11,8 +11,8 @@ "consumer_key", "column_break_5", "consumer_secret", - "oauth_token", - "oauth_secret", + "access_token", + "access_token_secret", "session_status" ], "fields": [ @@ -41,20 +41,6 @@ "label": "API Secret Key", "reqd": 1 }, - { - "fieldname": "oauth_token", - "fieldtype": "Data", - "hidden": 1, - "label": "OAuth Token", - "read_only": 1 - }, - { - "fieldname": "oauth_secret", - "fieldtype": "Password", - "hidden": 1, - "label": "OAuth Token Secret", - "read_only": 1 - }, { "fieldname": "column_break_5", "fieldtype": "Column Break" @@ -72,12 +58,26 @@ "label": "Session Status", "options": "Expired\nActive", "read_only": 1 + }, + { + "fieldname": "access_token", + "fieldtype": "Data", + "hidden": 1, + "label": "Access Token", + "read_only": 1 + }, + { + "fieldname": "access_token_secret", + "fieldtype": "Data", + "hidden": 1, + "label": "Access Token Secret", + "read_only": 1 } ], "image_field": "profile_pic", "issingle": 1, "links": [], - "modified": "2020-04-21 22:06:43.726798", + "modified": "2020-05-13 17:50:47.934776", "modified_by": "Administrator", "module": "CRM", "name": "Twitter Settings", diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py index 7616b4c027e..976a23dfc7d 100644 --- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py +++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py @@ -31,13 +31,13 @@ class TwitterSettings(Document): try: auth.get_access_token(oauth_verifier) - api = self.get_api() + api = self.get_api(auth.access_token, auth.access_token_secret) user = api.me() profile_pic = (user._json["profile_image_url"]).replace("_normal","") frappe.db.set_value(self.doctype, self.name, { - "oauth_token" : auth.access_token, - "oauth_secret" : auth.access_token_secret, + "access_token" : auth.access_token, + "access_token_secret" : auth.access_token_secret, "account_name" : user._json["screen_name"], "profile_pic" : profile_pic, "session_status" : "Active" @@ -49,11 +49,11 @@ class TwitterSettings(Document): frappe.msgprint(_("Error! Failed to get access token.")) frappe.throw(_('Invalid Consumer Key or Consumer Secret Key')) - def get_api(self): + def get_api(self, access_token, access_token_secret): # authentication of consumer key and secret auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret")) # authentication of access token and secret - auth.set_access_token(self.oauth_token, self.get_password(fieldname="oauth_secret")) + auth.set_access_token(access_token, access_token_secret) return tweepy.API(auth) @@ -67,13 +67,13 @@ class TwitterSettings(Document): def upload_image(self, media): media = get_file_path(media) - api = self.get_api() + api = self.get_api(self.access_token, self.access_token_secret) media = api.media_upload(media) return media.media_id def send_tweet(self, text, media_id=None): - api = self.get_api() + api = self.get_api(self.access_token, self.access_token_secret) try: if media_id: response = api.update_status(status = text, media_ids = [media_id]) From bd7e5358857b66f5025e38ab3cf82d589dec6506 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 13 May 2020 19:48:42 +0530 Subject: [PATCH 136/410] Fix: Set General Ledger 'Group By' filter as 'Group by Voucher(Consolidated)' when opened from Invoice (#21673) * fix for issue #21419 * changing group by filter to default Group by Voucher (Consolidated) --- erpnext/accounts/report/general_ledger/general_ledger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 1188beaa0f8..2aecd6b717d 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -53,7 +53,7 @@ frappe.query_reports["General Ledger"] = { "label": __("Voucher No"), "fieldtype": "Data", on_change: function() { - frappe.query_report.set_filter_value('group_by', ""); + frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)"); } }, { From 13096cdbfefd58f16f7e009a48343a5601f41221 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 22:02:56 +0530 Subject: [PATCH 137/410] fix: invalid conditional statement --- 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 5b16eb4640b..265969db1f0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1037,7 +1037,7 @@ class PurchaseInvoice(BuyingController): if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - elif outstanding_amount > 0 and due_date < nowdate: + if outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" From dde39c3d1aa323072de5703b8c30eabe07b3960c Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 14 May 2020 12:09:36 +0530 Subject: [PATCH 138/410] chore: Drop Python2 support (#21704) * chore: Drop Python2 support * test: Fix test redundancy by removing countries 3 countries seems ennough to test coa template feature --- .travis.yml | 17 ++--------------- erpnext/setup/doctype/company/test_company.py | 4 +--- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 213445b806e..77d427e5a50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ -dist: trusty - language: python +dist: trusty git: depth: 1 @@ -14,21 +13,10 @@ addons: jobs: include: - - name: "Python 2.7 Server Side Test" - python: 2.7 - script: bench --site test_site run-tests --app erpnext --coverage - - name: "Python 3.6 Server Side Test" python: 3.6 script: bench --site test_site run-tests --app erpnext --coverage - - name: "Python 2.7 Patch Test" - python: 2.7 - before_script: - - wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz - - bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz - script: bench --site test_site migrate - - name: "Python 3.6 Patch Test" python: 3.6 before_script: @@ -40,8 +28,7 @@ install: - cd ~ - nvm install 10 - - git clone https://github.com/frappe/bench --depth 1 - - pip install -e ./bench + - pip install frappe-bench - git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1 - bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index b37cc17ba98..29f6c3731d7 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -47,9 +47,7 @@ class TestCompany(unittest.TestCase): frappe.delete_doc("Company", "COA from Existing Company") def test_coa_based_on_country_template(self): - countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France", - "Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore", - "Brazil", "Argentina", "Hungary", "Taiwan"] + countries = ["Canada", "Germany", "France"] for country in countries: templates = get_charts_for_country(country) From 021acd0a0113a8a46013d8766cd50f45f3009977 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 14 May 2020 13:20:43 +0530 Subject: [PATCH 139/410] chore: Stock Ageing and Item Shortage Reports with Charts --- .../item_shortage_report.js | 26 +++ .../item_shortage_report.json | 41 ++--- .../item_shortage_report.py | 162 ++++++++++++++++++ .../stock/report/stock_ageing/stock_ageing.py | 32 +++- 4 files changed, 240 insertions(+), 21 deletions(-) create mode 100644 erpnext/stock/report/item_shortage_report/item_shortage_report.js create mode 100644 erpnext/stock/report/item_shortage_report/item_shortage_report.py diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.js b/erpnext/stock/report/item_shortage_report/item_shortage_report.js new file mode 100644 index 00000000000..ca42a331e91 --- /dev/null +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.js @@ -0,0 +1,26 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Item Shortage Report"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname": "warehouse", + "label": __("Warehouse"), + "fieldtype": "MultiSelectList", + "width": "100", + get_data: function(txt) { + return frappe.db.get_link_options('Warehouse', txt); + } + } + ] +}; diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.json b/erpnext/stock/report/item_shortage_report/item_shortage_report.json index 577a8530b7e..17285c09de4 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.json +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.json @@ -1,29 +1,30 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-08-20 13:43:30", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Bin.projected_qty\", \"sort_order\": \"asc\", \"sort_by_next\": \"\", \"filters\": [[\"Bin\", \"projected_qty\", \"<\", \"0\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"warehouse\", \"Bin\"], [\"item_code\", \"Bin\"], [\"actual_qty\", \"Bin\"], [\"ordered_qty\", \"Bin\"], [\"planned_qty\", \"Bin\"], [\"reserved_qty\", \"Bin\"], [\"projected_qty\", \"Bin\"]]}", - "modified": "2017-02-24 20:00:46.439935", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Shortage Report", - "owner": "Administrator", - "query": "SELECT bin.warehouse as \"Warehouse:Link/Warehouse:150\",\n\tbin.item_code as \"Item Code:Link/Item:100\",\n\tbin.actual_qty as \"Actual Quantity:Float:120\",\n\tbin.ordered_qty as \"Ordered Quantity:Float:120\",\n\tbin.planned_qty as \"Planned Quantity:Float:120\",\n\tbin.reserved_qty as \"Reserved Quantity:Float:120\",\n\tbin.projected_qty as \"Project Quantity:Float:120\",\n\titem.item_name as \"Item Name:Data:150\",\n\titem.description as \"Description::200\"\nFROM tabBin as bin\nINNER JOIN tabItem as item\nON bin.item_code=item.name\nWHERE bin.projected_qty<0\nORDER BY bin.projected_qty;", - "ref_doctype": "Bin", - "report_name": "Item Shortage Report", - "report_type": "Query Report", + "add_total_row": 0, + "creation": "2013-08-20 13:43:30", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Bin.projected_qty\", \"sort_order\": \"asc\", \"sort_by_next\": \"\", \"filters\": [[\"Bin\", \"projected_qty\", \"<\", \"0\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"warehouse\", \"Bin\"], [\"item_code\", \"Bin\"], [\"actual_qty\", \"Bin\"], [\"ordered_qty\", \"Bin\"], [\"planned_qty\", \"Bin\"], [\"reserved_qty\", \"Bin\"], [\"projected_qty\", \"Bin\"]]}", + "modified": "2020-05-14 12:32:07.158991", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Shortage Report", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Bin", + "report_name": "Item Shortage Report", + "report_type": "Script Report", "roles": [ { "role": "Sales User" - }, + }, { "role": "Purchase User" - }, + }, { "role": "Stock User" } diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py new file mode 100644 index 00000000000..07749ebec57 --- /dev/null +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -0,0 +1,162 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns = get_columns() + conditions = get_conditions(filters) + data = get_data(conditions, filters) + + if not data: + return [], [] + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data + +def get_conditions(filters): + conditions = "" + + if filters.get("warehouse"): + conditions += "AND warehouse in %(warehouse)s" + if filters.get("company"): + conditions += "AND company = %(company)s" + + return conditions + +def get_data(conditions, filters): + data = frappe.db.sql(""" + SELECT + bin.warehouse, + bin.item_code, + bin.actual_qty , + bin.ordered_qty , + bin.planned_qty , + bin.reserved_qty , + bin.reserved_qty_for_production, + bin.projected_qty , + warehouse.company, + item.item_name , + item.description + FROM + `tabBin` bin, + `tabWarehouse` warehouse, + `tabItem` item + WHERE + bin.projected_qty<0 + AND warehouse.name = bin.warehouse + AND bin.item_code=item.name + {0} + ORDER BY bin.projected_qty;""".format(conditions), filters, as_dict=1) + + return data + +def get_chart_data(data): + labels, datapoints = [], [] + + for row in data: + labels.append(row.get("item_code")) + datapoints.append(row.get("projected_qty")) + + if len(data) > 10: + labels = labels[:10] + datapoints = datapoints[:10] + + return { + "data": { + "labels": labels, + "datasets":[ + { + "name": _("Projected Qty"), + "values": datapoints + } + ] + }, + "type": "bar" + } + +def get_columns(): + columns = [ + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 150 + }, + { + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 + }, + { + "label": _("Actual Quantity"), + "fieldname": "actual_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Ordered Quantity"), + "fieldname": "ordered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Planned Quantity"), + "fieldname": "planned_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Reserved Quantity"), + "fieldname": "reserved_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Reserved Quantity for Production"), + "fieldname": "reserved_qty_for_production", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Projected Quantity"), + "fieldname": "projected_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("Description"), + "fieldname": "description", + "fieldtype": "Data", + "width": 120 + } + ] + + return columns + + diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 803a5c81a3b..4ce967d972a 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -37,7 +37,9 @@ def execute(filters=None): data.append(row) - return columns, data + chart_data = get_chart_data(data, filters) + + return columns, data, None, chart_data def get_average_age(fifo_queue, to_date): batch_age = age_qty = total_qty = 0.0 @@ -230,3 +232,31 @@ def get_sle_conditions(filters): where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt)) return "and {}".format(" and ".join(conditions)) if conditions else "" + +def get_chart_data(data, filters): + labels, datapoints = [], [] + + if filters.get("show_warehouse_wise_stock"): + return {} + + if len(data) > 10: + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[6]) + + print(labels) + print(datapoints) + return { + "data" : { + "labels": labels, + "datasets": [ + { + "name": _("Average Age"), + "values": datapoints + } + ] + }, + "type" : "bar" + } \ No newline at end of file From 10bd2417ff9d01866e48cf2569532903c2e3dfb6 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 14 May 2020 15:35:18 +0530 Subject: [PATCH 140/410] feat: Custom Dashboard 'Warehouse wise Stock Value' with Chart Source --- .../stock/dashboard_chart_source/__init__.py | 0 .../warehouse_wise_stock_value/__init__.py | 0 .../warehouse_wise_stock_value.js | 14 ++++++ .../warehouse_wise_stock_value.json | 13 ++++++ .../warehouse_wise_stock_value.py | 44 +++++++++++++++++++ .../stock/report/stock_ageing/stock_ageing.py | 2 - 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 erpnext/stock/dashboard_chart_source/__init__.py create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py diff --git a/erpnext/stock/dashboard_chart_source/__init__.py b/erpnext/stock/dashboard_chart_source/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js new file mode 100644 index 00000000000..a4137547f7e --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Warehouse wise Stock Value"] = { + method: "erpnext.stock.dashboard_chart_source.warehouse_wise_stock_value.warehouse_wise_stock_value.get", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json new file mode 100644 index 00000000000..6d967c0fc00 --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json @@ -0,0 +1,13 @@ +{ + "creation": "2020-05-14 14:27:44.108017", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2020-05-14 14:27:44.108017", + "modified_by": "Administrator", + "module": "Stock", + "name": "Warehouse wise Stock Value", + "owner": "Administrator", + "source_name": "Warehouse wise Stock Value ", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py new file mode 100644 index 00000000000..0ee0a6cc77b --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -0,0 +1,44 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils.dashboard import cache_source +from erpnext.stock.utils import get_stock_value_from_bin + +@frappe.whitelist() +@cache_source +def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None): + labels, datapoints = [], [] + filters = frappe.parse_json(filters) + + warehouse_filters = [['is_group', '=', 0]] + if filters and filters.get("company"): + warehouse_filters.append(['company', '=', filters.get("company")]) + + warehouses = frappe.get_list("Warehouse", fields=['name'], filters=warehouse_filters, order_by='name') + + for wh in warehouses: + balance = get_stock_value_from_bin(warehouse=wh.name) + wh["balance"] = balance[0][0] + + warehouses = [x for x in warehouses if not (x.get('balance') == None)] + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) + + if len(sorted_warehouse_map) > 10: + sorted_warehouse_map = sorted_warehouse_map[:10] + + for warehouse in sorted_warehouse_map: + labels.append(_(warehouse.get("name"))) + datapoints.append(warehouse.get("balance")) + + return{ + "labels": labels, + "datasets": [{ + "name": _("Stock Value"), + "values": datapoints + }], + "type": "bar" + } \ No newline at end of file diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 4ce967d972a..48f22c2c24d 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -246,8 +246,6 @@ def get_chart_data(data, filters): labels.append(row[0]) datapoints.append(row[6]) - print(labels) - print(datapoints) return { "data" : { "labels": labels, From 10df3d5081c54d5ee70bdf69c81c3d70acb35169 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 14 May 2020 17:15:16 +0530 Subject: [PATCH 141/410] fix: Get basic and hra component from db, not from cache --- erpnext/regional/india/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 33098587c2a..732780a0a33 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -251,8 +251,7 @@ def get_tax_template_for_sez(party_details, master_doctype, company, party_type) def calculate_annual_eligible_hra_exemption(doc): - basic_component = frappe.get_cached_value('Company', doc.company, "basic_component") - hra_component = frappe.get_cached_value('Company', doc.company, "hra_component") + basic_component, hra_component = frappe.db.get_value('Company', doc.company, ["basic_component", "hra_component"]) if not (basic_component and hra_component): frappe.throw(_("Please mention Basic and HRA component in Company")) annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 From 200f80c3d368884000ebd8b5942e1e9ea28d0301 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 14 May 2020 17:18:29 +0530 Subject: [PATCH 142/410] fix: Added submit permission in employee other income --- .../employee_other_income.json | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/hr/doctype/employee_other_income/employee_other_income.json index 2dd6c10988d..8abfe1e93a8 100644 --- a/erpnext/hr/doctype/employee_other_income/employee_other_income.json +++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.json @@ -76,25 +76,15 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-19 18:06:45.361830", + "modified": "2020-05-14 17:17:38.883126", "modified_by": "Administrator", "module": "HR", "name": "Employee Other Income", "owner": "Administrator", "permissions": [ { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -104,9 +94,12 @@ "report": 1, "role": "HR Manager", "share": 1, + "submit": 1, "write": 1 }, { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -116,9 +109,12 @@ "report": 1, "role": "HR User", "share": 1, + "submit": 1, "write": 1 }, { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -128,6 +124,7 @@ "report": 1, "role": "Employee", "share": 1, + "submit": 1, "write": 1 } ], From 39cb749f955f0fe40dc2289cdbc32bcb7f9ba939 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 14 May 2020 18:25:58 +0530 Subject: [PATCH 143/410] fix: add heatmap_year parameter to get --- .../account_balance_timeline/account_balance_timeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5decccb4869..39bf4b053a7 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 @@ -14,7 +14,7 @@ from frappe.utils.nestedset import get_descendants_of @frappe.whitelist() @cache_source def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, - to_date = None, timespan = None, time_interval = None): + to_date = None, timespan = None, time_interval = None, heatmap_year = None): if chart_name: chart = frappe.get_doc('Dashboard Chart', chart_name) else: From 79a8bd1cdff67149d4a2bcc32c06b35f46fbb09b Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 10 May 2020 23:36:33 +0530 Subject: [PATCH 144/410] CRM Onboarding --- erpnext/crm/dashboard_fixtures.py | 203 ++++++++++++++++++ erpnext/crm/desk_page/crm/crm.json | 10 +- erpnext/crm/module_onboarding/crm/crm.json | 45 ++++ erpnext/crm/onboarding/crm/crm.json | 45 ++++ .../create_and_send_quotation.json | 17 ++ .../create_lead/create_lead.json | 17 ++ .../create_opportunity.json | 17 ++ .../introduction_to_crm.json | 17 ++ .../start_campaign/start_campaign.json | 17 ++ 9 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 erpnext/crm/dashboard_fixtures.py create mode 100644 erpnext/crm/module_onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json create mode 100644 erpnext/crm/onboarding_step/create_lead/create_lead.json create mode 100644 erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json create mode 100644 erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json create mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py new file mode 100644 index 00000000000..5b1ac9bf5e2 --- /dev/null +++ b/erpnext/crm/dashboard_fixtures.py @@ -0,0 +1,203 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, erpnext, json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards() + }) + +def get_dashboards(): + return [{ + "doctype": "Dashboard", + "name": "CRM", + "dashboard_name": "CRM", + "charts": [ + { "chart": "Lead", "width": "Full" }, + { "chart": "Opportunity", "width": "Full"}, + { "chart": "Campaign", "width": "Half" }, + { "chart": "Opportunities Won", "width": "Half" }, + { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Territory Wise Sales", "width": "Half"}, + { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Lead Source", "width": "Half"} + ], + "cards": [ + { "card": "New Lead" }, + { "card": "New Opportunity" }, + { "card": "Won Opportunity" }, + ] + }] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_charts(): + company = get_company_for_dashboards() + + return [{ + "name": "Lead", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Lead", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Lead", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Opportunity", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunity", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Campaign", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Campaign", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Campaign", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Line", + "color": "#428b46" + }, + { + "name": "Opportunities Won", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunities Won", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar" + }, + { + "name": "Territory Wise Opportunity", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Opportunity", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "type": "Line" + }, + { + "name": "Territory Wise Sales", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Sum", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Sales", + "aggregate_function_based_on": "opportunity_amount", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar", + "color": "#7575ff" + }, + { + "name": "Qualified For Call", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Qualified For Call", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), + "type": "Line", + "color": "#fff168" + }, + { + "name": "Lead Source", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "source", + "chart_name": "Lead Source", + "document_type": "Lead", + 'is_public': 1, + "owner": "Administrator", + "type": "Donut" + }] + +def get_number_cards(): + return [{ + "doctype": "Number Card", + "document_type": "Lead", + "name": "New Lead", + "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "function": "Count", + "is_public": 1, + "label": "New Lead", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "New Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), + "function": "Count", + "is_public": 1, + "label": "New Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), + "function": "Count", + "is_public": 1, + "label": "Won Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index ca13d6abb66..80d3d18de97 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -27,21 +27,25 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Territory Wise Sales" + } + ], "creation": "2020-01-23 14:48:30.183272", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-04-27 22:32:26.682911", + "modified": "2020-05-14 17:20:07.636751", "modified_by": "Administrator", "module": "CRM", "name": "CRM", + "onboarding": "CRM", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json new file mode 100644 index 00000000000..9129c2625a0 --- /dev/null +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 17:41:11.083917", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Create Opportunity" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding/crm/crm.json b/erpnext/crm/onboarding/crm/crm.json new file mode 100644 index 00000000000..016a8307dd7 --- /dev/null +++ b/erpnext/crm/onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-09 23:42:50.901548", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Convert Lead to Customer" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json new file mode 100644 index 00000000000..0997017933a --- /dev/null +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:42:46.592075", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:30:07.887411", + "modified_by": "Administrator", + "name": "Create and Send Quotation", + "owner": "Administrator", + "reference_document": "Quotation", + "title": "Create and Send Quotation" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json new file mode 100644 index 00000000000..b1076e0bf95 --- /dev/null +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:40:25.192503", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:36.441387", + "modified_by": "Administrator", + "name": "Create Lead", + "owner": "Administrator", + "reference_document": "Lead", + "title": "Create Lead" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json new file mode 100644 index 00000000000..20949a435bc --- /dev/null +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:38:27.496696", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:38:27.496696", + "modified_by": "Administrator", + "name": "Create Opportunity", + "owner": "Administrator", + "reference_document": "Opportunity", + "title": "Create Opportunity" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json new file mode 100644 index 00000000000..c923a77e1b9 --- /dev/null +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -0,0 +1,17 @@ +{ + "action": "Watch Video", + "creation": "2020-05-09 23:37:08.926812", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:16.448676", + "modified_by": "Administrator", + "name": "Introduction to CRM", + "owner": "Administrator", + "title": "Introduction to CRM", + "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json new file mode 100644 index 00000000000..53c3becea3b --- /dev/null +++ b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:38:10.284957", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:54.285958", + "modified_by": "Administrator", + "name": "Start Campaign", + "owner": "Administrator", + "reference_document": "Campaign", + "title": "Start Campaign" +} \ No newline at end of file From 8bbac6defc464dc6a7ad4b71675b3cefe4064225 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 14 May 2020 22:57:11 +0530 Subject: [PATCH 145/410] fix: Onboarding and Dashboard for accounts module --- erpnext/accounts/dashboard_fixtures.py | 60 ++++++++++-- .../desk_page/accounting/accounting.json | 20 ++-- .../module_onboarding/accounts/accounts.json | 51 ++++++++++ .../chart_of_accounts/chart_of_accounts.json | 20 ++++ .../configure_account_settings.json | 19 ++++ .../create_a_customer/create_a_customer.json | 19 ++++ .../create_a_product/create_a_product.json | 19 ++++ .../create_a_supplier/create_a_supplier.json | 19 ++++ .../create_your_first_purchase_invoice.json | 19 ++++ .../create_your_first_sales_invoice.json | 19 ++++ .../setup_taxes/setup_taxes.json | 19 ++++ .../budget_variance_report.py | 96 ++++++++++++++++--- 12 files changed, 347 insertions(+), 33 deletions(-) create mode 100644 erpnext/accounts/module_onboarding/accounts/accounts.json create mode 100644 erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json create mode 100644 erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json create mode 100644 erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json create mode 100644 erpnext/accounts/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json create mode 100644 erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json create mode 100644 erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json create mode 100644 erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 46d1d6a66af..8b9eca5b2d3 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -4,6 +4,8 @@ import frappe import json from frappe.utils import nowdate, add_months +from frappe import _ +from erpnext.accounts.utils import get_fiscal_year def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -18,11 +20,11 @@ def get_company_for_dashboards(): def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts() + "charts": get_charts(), + "number_cards": get_number_cards() }) def get_dashboards(): - return [{ "name": "Accounts Dashboard", "dashboard_name": "Accounts", @@ -33,13 +35,19 @@ def get_dashboards(): { "chart": "Outgoing Bills"}, { "chart": "Accounts Receivable Ageing"}, { "chart": "Accounts Payable Ageing"}, + { "chart": "Budget Variance", "width": "Full"}, { "chart": "Bank Balance", "width": "Full"}, + ], + "cards": [ + {"card": "Total Payment Received"} ] }] def get_charts(): company = frappe.get_doc("Company", get_company_for_dashboards()) bank_account = company.default_bank_account or get_account("Bank", company.name) + fiscal_year = get_fiscal_year(date=nowdate()) + default_cost_center = company.cost_center return [ { @@ -58,14 +66,14 @@ def get_charts(): "type": "Bar", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Profit and Loss", + "chart_name": _("Profit and Loss"), "is_custom": 1 }, { "doctype": "Dashboard Chart", "time_interval": "Monthly", "name": "Incoming Bills", - "chart_name": "Incoming Bills (Purchase Invoice)", + "chart_name": _("Incoming Bills (Purchase Invoice)"), "timespan": "Last Year", "color": "#a83333", "value_based_on": "base_grand_total", @@ -82,7 +90,7 @@ def get_charts(): "doctype": "Dashboard Chart", "name": "Outgoing Bills", "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", + "chart_name": _("Outgoing Bills (Sales Invoice)"), "timespan": "Last Year", "color": "#7b933d", "value_based_on": "base_grand_total", @@ -112,7 +120,7 @@ def get_charts(): "type": "Donut", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Accounts Receivable Ageing", + "chart_name": _("Accounts Receivable Ageing"), "is_custom": 1 }, { @@ -128,11 +136,29 @@ def get_charts(): "range2": 60, "range3": 90, "range4": 120 - }), + }), "type": "Donut", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Accounts Payable Ageing", + "chart_name": _("Accounts Payable Ageing"), + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Budget Variance", + "owner": "Administrator", + "report_name": "Budget Variance Report", + "filters_json": json.dumps({ + "company": company.name, + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], + "period": "Monthly", + "budget_against": "Cost Center" + }), + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": _("Budget Variance"), "is_custom": 1 }, { @@ -149,5 +175,21 @@ def get_charts(): "type": "Line", "width": "Half" }, - + ] + +def get_number_cards(): + return [ + { + "doctype": "Number Card", + "document_type": "Payment Entry", + "name": "Total Payment Received", + "filters_json": json.dumps([]), + "label": _("Total Payment Received"), + "function": "Sum", + "aggregate_function_based_on": "base_received_amount", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } ] diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 648ebe81a15..682eb8fcede 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -82,12 +82,7 @@ } ], "category": "Modules", - "charts": [ - { - "chart_name": "Bank Balance", - "label": "Bank Balance" - } - ], + "charts": [], "creation": "2020-03-02 15:41:59.515192", "developer_mode_only": 0, "disable_user_customization": 0, @@ -97,10 +92,11 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-12 17:20:27.573449", + "modified": "2020-05-14 22:28:25.262409", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", + "onboarding": "Accounts", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -120,6 +116,11 @@ "link_to": "Purchase Invoice", "type": "DocType" }, + { + "label": "Accounts Dashboard", + "link_to": "Accounts Dashboard", + "type": "Dashboard" + }, { "label": "Journal Entry", "link_to": "Journal Entry", @@ -140,11 +141,6 @@ "link_to": "General Ledger", "type": "Report" }, - { - "label": "Profit and Loss Statement", - "link_to": "Profit and Loss Statement", - "type": "Report" - }, { "label": "Trial Balance", "link_to": "Trial Balance", diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json new file mode 100644 index 00000000000..12da4400284 --- /dev/null +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -0,0 +1,51 @@ +{ + "allow_roles": [ + { + "role": "Accounts Manager" + }, + { + "role": "Accounts User" + } + ], + "creation": "2020-05-13 19:03:32.564049", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 22:11:06.475938", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts", + "owner": "Administrator", + "steps": [ + { + "step": "Chart Of Accounts" + }, + { + "step": "Setup Taxes" + }, + { + "step": "Create a Product" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Create Your First Purchase Invoice" + }, + { + "step": "Create a Customer" + }, + { + "step": "Create Your First Sales Invoice" + }, + { + "step": "Configure Account Settings" + } + ], + "subtitle": "Accounts, invoices and taxation.", + "success_message": "The Accounts module is now set up!", + "title": "Let's Setup Your Accounts and Taxes.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json new file mode 100644 index 00000000000..cbd022bfdbe --- /dev/null +++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-13 19:58:20.928127", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:40:28.410447", + "modified_by": "Administrator", + "name": "Chart Of Accounts", + "owner": "Administrator", + "path": "Tree/Account", + "reference_document": "Account", + "show_full_form": 0, + "title": "Review Chart Of Accounts", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json new file mode 100644 index 00000000000..c8be357de0a --- /dev/null +++ b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:53:00.876946", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-14 18:06:25.212923", + "modified_by": "Administrator", + "name": "Configure Account Settings", + "owner": "Administrator", + "reference_document": "Accounts Settings", + "show_full_form": 1, + "title": "Configure Account Settings", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json new file mode 100644 index 00000000000..bb396d268ae --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:46:41.831517", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:46:41.831517", + "modified_by": "Administrator", + "name": "Create a Customer", + "owner": "Administrator", + "reference_document": "Customer", + "show_full_form": 0, + "title": "Create a Customer", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..450bee1f40b --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:45:28.554605", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:45:28.554605", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json new file mode 100644 index 00000000000..7a64224bd43 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 22:09:10.043554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 22:09:10.043554", + "modified_by": "Administrator", + "name": "Create a Supplier", + "owner": "Administrator", + "reference_document": "Supplier", + "show_full_form": 0, + "title": "Create a Supplier", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json new file mode 100644 index 00000000000..3a2b8d39253 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 22:10:07.049704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 22:10:07.049704", + "modified_by": "Administrator", + "name": "Create Your First Purchase Invoice", + "owner": "Administrator", + "reference_document": "Purchase Invoice", + "show_full_form": 1, + "title": "Create Your First Purchase Invoice ", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json new file mode 100644 index 00000000000..473de5079f5 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:48:21.019019", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:48:21.019019", + "modified_by": "Administrator", + "name": "Create Your First Sales Invoice", + "owner": "Administrator", + "reference_document": "Sales Invoice", + "show_full_form": 1, + "title": "Create Your First Sales Invoice ", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json new file mode 100644 index 00000000000..8e0006762d1 --- /dev/null +++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-13 19:29:43.844463", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:40:16.014413", + "modified_by": "Administrator", + "name": "Setup Taxes", + "owner": "Administrator", + "reference_document": "Sales Taxes and Charges Template", + "show_full_form": 1, + "title": "Lets create a Tax Template for Sales ", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 49c1d0f2ccd..05dc2826611 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -56,14 +56,26 @@ def execute(filters=None): row += totals data.append(row) - return columns, data + chart = get_chart_data(filters, columns, data) + return columns, data, None, chart def get_columns(filters): columns = [ - _(filters.get("budget_against")) - + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150" + { + 'label': _(filters.get("budget_against")), + 'fieldtype': 'Link', + 'fieldname': 'budget_against', + 'options': filters.get('budget_against'), + 'width': 150 + }, + { + 'label': _('Account'), + 'fieldname': 'Account', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 150 + } ] group_months = False if filters["period"] == "Monthly" else True @@ -79,7 +91,12 @@ def get_columns(filters): _("Variance ") + " " + str(year[0]) ] for label in labels: - columns.append(label + ":Float:150") + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) else: for label in [ _("Budget") + " (%s)" + " " + str(year[0]), @@ -95,14 +112,23 @@ def get_columns(filters): else: label = label % formatdate(from_date, format_string="MMM") - columns.append(label + ":Float:150") + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) if filters["period"] != "Yearly": - return columns + [ - _("Total Budget") + ":Float:150", - _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150" - ] + for label in [_("Total Budget"), _("Total Actual"), _("Total Variance")]: + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) + + return columns else: return columns @@ -173,7 +199,7 @@ def get_dimension_target_details(filters): filters.budget_against, filters.company, ] - + filters.get("budget_against_filter") + + (filters.get("budget_against_filter") or []) ), as_dict=True) @@ -305,3 +331,49 @@ def get_fiscal_years(filters): }) return fiscal_year + +def get_chart_data(filters, columns, data): + + if not data: + return None + + labels = [] + + fiscal_year = get_fiscal_years(filters) + group_months = False if filters["period"] == "Monthly" else True + + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters['period'] == 'Yearly': + labels.append(year[0]) + else: + if group_months: + label = formatdate(from_date, format_string="MMM") + "-" \ + + formatdate(to_date, format_string="MMM") + labels.append(label) + else: + label = formatdate(from_date, format_string="MMM") + labels.append(label) + + no_of_columns = len(labels) + + budget_values, actual_values = [0] * no_of_columns, [0] * no_of_columns + for d in data: + values = d[2:] + index = 0 + + for i in range(no_of_columns): + budget_values[i] += values[index] + actual_values[i] += values[index+1] + index += 3 + + return { + 'data': { + 'labels': labels, + 'datasets': [ + {'name': 'Budget', 'chartType': 'bar', 'values': budget_values}, + {'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values} + ] + } + } + From bb95cd5fd83ea5572940eed4ffde94891065589d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 01:33:43 +0530 Subject: [PATCH 146/410] chore: Added Dynamic Charts to DN and PR Trends Reports --- erpnext/public/js/purchase_trends_filters.js | 5 ++- erpnext/public/js/sales_trends_filters.js | 5 ++- .../warehouse_wise_stock_value.py | 2 +- .../delivery_note_trends.py | 31 +++++++++++++++++-- .../purchase_receipt_trends.py | 30 +++++++++++++++++- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index cd767f5d167..d95507b7d1b 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -51,7 +51,10 @@ erpnext.get_purchase_trends_filters = function() { { "value": "Supplier Group", "label": __("Supplier Group") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1, + } }, { "fieldname":"group_by", diff --git a/erpnext/public/js/sales_trends_filters.js b/erpnext/public/js/sales_trends_filters.js index b272fdd5fb0..b9c4dca9130 100644 --- a/erpnext/public/js/sales_trends_filters.js +++ b/erpnext/public/js/sales_trends_filters.js @@ -27,7 +27,10 @@ erpnext.get_sales_trends_filters = function() { { "value": "Territory", "label": __("Territory") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1, + } }, { "fieldname":"group_by", diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 0ee0a6cc77b..da2f9350c6d 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -10,7 +10,7 @@ from erpnext.stock.utils import get_stock_value_from_bin @frappe.whitelist() @cache_source def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, - to_date = None, timespan = None, time_interval = None): + to_date = None, timespan = None, time_interval = None, heatmap_year = None): labels, datapoints = [], [] filters = frappe.parse_json(filters) diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 27cf6b66ccb..2108b51afc8 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,5 +11,31 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Delivery Note") data = get_data(filters, conditions) - - return conditions["columns"], data \ No newline at end of file + + chart_data = get_chart_data(data) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data): + labels, datapoints = [], [] + + if len(data) > 10: + data = sorted(data, key = lambda i: i[-1],reverse=True) + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[-1]) + + return { + "data": { + "labels" : labels, + "datasets" : [ + { + "name": _("Total Revenue"), + "values": datapoints + } + ] + }, + "type" : "bar" + } \ No newline at end of file diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 0e58920725e..ac235f74e1c 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -11,4 +12,31 @@ def execute(filters=None): conditions = get_columns(filters, "Purchase Receipt") data = get_data(filters, conditions) - return conditions["columns"], data \ No newline at end of file + chart_data = get_chart_data(data) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data): + labels, datapoints = [], [] + + if len(data) > 10: + data = sorted(data, key = lambda i: i[-1],reverse=True) + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[-1]) + + return { + "data": { + "labels" : labels, + "datasets" : [ + { + "name": _("Total Expenditure"), + "values": datapoints + } + ] + }, + "type" : "bar", + "colors":["#5e64ff"] + } \ No newline at end of file From 3f7678416f76cb86157c1df4a744b064d55190de Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 03:58:09 +0530 Subject: [PATCH 147/410] chore: Stock Onboarding --- erpnext/stock/desk_page/stock/stock.json | 54 ++++++++++++++----- .../doctype/stock_settings/stock_settings.js | 34 ++++++++++++ .../stock/module_onboarding/stock/stock.json | 54 +++++++++++++++++++ .../buying_settings/buying_settings.json | 19 +++++++ .../create_a_price_list.json | 19 +++++++ .../create_a_product/create_a_product.json | 19 +++++++ .../create_a_stock_entry.json | 19 +++++++ .../create_a_warehouse.json | 19 +++++++ ...oduction_to_price_list_and_item_price.json | 19 +++++++ .../introduction_to_stock_entry.json | 19 +++++++ .../stock_settings/stock_settings.json | 19 +++++++ 11 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 erpnext/stock/module_onboarding/stock/stock.json create mode 100644 erpnext/stock/onboarding_step/buying_settings/buying_settings.json create mode 100644 erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json create mode 100644 erpnext/stock/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json create mode 100644 erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json create mode 100644 erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json create mode 100644 erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json create mode 100644 erpnext/stock/onboarding_step/stock_settings/stock_settings.json diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 38475a6f269..6b93449ecd4 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -1,9 +1,14 @@ { "cards": [ + { + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Stock Transactions", - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,12 +18,7 @@ { "hidden": 0, "label": "Settings", - "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -41,34 +41,45 @@ "links": "[\n {\n \"dependencies\": [\n \"Material Request\"\n ],\n \"doctype\": \"Material Request\",\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Transferred\",\n \"name\": \"Requested Items To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Batch Item Expiry Status\",\n \"name\": \"Batch Item Expiry Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Price List\"\n ],\n \"doctype\": \"Price List\",\n \"is_query_report\": true,\n \"label\": \"Item Prices\",\n \"name\": \"Item Prices\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Itemwise Recommended Reorder Level\",\n \"name\": \"Itemwise Recommended Reorder Level\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Variant Details\",\n \"name\": \"Item Variant Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock and Account Value Comparison\",\n \"name\": \"Stock and Account Value Comparison\",\n \"type\": \"report\"\n }\n]" } ], + "cards_label": "Masters & Reports", "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Warehouse wise Stock Value" + } + ], "creation": "2020-03-02 15:43:10.096528", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-04-01 11:28:51.148421", + "modified": "2020-05-15 04:11:36.326013", "modified_by": "Administrator", "module": "Stock", "name": "Stock", + "onboarding": "Stock", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [ { + "color": "#cef6d1", + "format": "{} available", "label": "Item", "link_to": "Item", + "stats_filter": "{\n \"disabled\" : 0\n}", "type": "DocType" }, { - "label": "Pricing Rule", - "link_to": "Pricing Rule", + "color": "#ffe8cd", + "format": "{} Pending", + "label": "Material Request", + "link_to": "Material Request", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}", "type": "DocType" }, { @@ -76,6 +87,22 @@ "link_to": "Stock Entry", "type": "DocType" }, + { + "color": "#ffe8cd", + "format": "{} to Bill", + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} to Bill", + "label": "Delivery Note", + "link_to": "Delivery Note", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, { "label": "Stock Ledger", "link_to": "Stock Ledger", @@ -86,5 +113,6 @@ "link_to": "Stock Balance", "type": "Report" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index cc0e2cfc425..81c60679467 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -15,3 +15,37 @@ frappe.ui.form.on('Stock Settings', { frm.set_query("sample_retention_warehouse", filters); } }); + +frappe.tour['Stock Settings'] = [ + { + fieldname: "item_naming_by", + title: __("Item Naming By"), + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") + }, + { + fieldname: "valuation_method", + title: __("Valuation Method"), + description: __("Choose between FIFO and Moving Average Valuation Methods. Click") + "here" + __("to understand them") + }, + { + fieldname: "show_barcode_field", + title: __("Show Barcode Field"), + description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") + }, + { + fieldname: "default_warehouse", + title: __("Default Warehouse"), + description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master:") + }, + { + fieldname: "allow_negative_stock", + title: __("Allow Negative Stock"), + description: __("This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.") + + }, + { + fieldname: "automatically_set_serial_nos_based_on_fifo", + title: __("Automatically Set Serial Nos based on FIFO"), + description: __("Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.") + } +]; diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json new file mode 100644 index 00000000000..28d8f672850 --- /dev/null +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-15 03:18:44.400108", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-15 04:02:23.634655", + "modified_by": "Administrator", + "module": "Stock", + "name": "Stock", + "owner": "Administrator", + "steps": [ + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Stock Settings" + }, + { + "step": "Introduction to Stock Entry" + }, + { + "step": "Create a Stock Entry" + }, + { + "step": "Introduction to Price List and Item Price" + }, + { + "step": "Create a Price List" + } + ], + "subtitle": "Inventory, Warehouses, Analysis and more.", + "success_message": "The Stock Module is all set up!", + "title": "Let's Setup the Stock Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/buying_settings/buying_settings.json b/erpnext/stock/onboarding_step/buying_settings/buying_settings.json new file mode 100644 index 00000000000..a788ccd4cc9 --- /dev/null +++ b/erpnext/stock/onboarding_step/buying_settings/buying_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Update Settings", + "creation": "2020-05-06 15:53:44.667414", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:06.323797", + "modified_by": "Administrator", + "name": "Buying Settings", + "owner": "Administrator", + "reference_document": "Buying Settings", + "show_full_form": 0, + "title": "Configure Buying Settings.", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json b/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json new file mode 100644 index 00000000000..ce5b5ecf86e --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 03:26:41.917046", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:33:09.305991", + "modified_by": "Administrator", + "name": "Create a Price List", + "owner": "Administrator", + "reference_document": "Price List", + "show_full_form": 1, + "title": "Create a Price List", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_product/create_a_product.json b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..d2068e167b7 --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json new file mode 100644 index 00000000000..2b83f657d6e --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 03:20:16.277043", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:30:58.047696", + "modified_by": "Administrator", + "name": "Create a Stock Entry", + "owner": "Administrator", + "reference_document": "Stock Entry", + "show_full_form": 1, + "title": "Create a Stock Entry", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json new file mode 100644 index 00000000000..3269125efc2 --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:00:03.027704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:27:44.128737", + "modified_by": "Administrator", + "name": "Create a Warehouse", + "owner": "Administrator", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Purchase Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json new file mode 100644 index 00000000000..f75523ab298 --- /dev/null +++ b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-05-15 03:26:01.386069", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:31:01.267728", + "modified_by": "Administrator", + "name": "Introduction to Price List and Item Price", + "owner": "Administrator", + "show_full_form": 0, + "title": "Let's take a brief look at Price List and Item Price", + "validate_action": 1, + "video_url": "https://www.youtube.com/watch?v=lY6hAQM1I28" +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json new file mode 100644 index 00000000000..229bcd49cf8 --- /dev/null +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-05-15 02:47:17.958806", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:30:39.606147", + "modified_by": "Administrator", + "name": "Introduction to Stock Entry", + "owner": "Administrator", + "show_full_form": 0, + "title": "Introduction to the backbone of Stock, Stock Entry.", + "validate_action": 1, + "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json new file mode 100644 index 00000000000..7591bff5386 --- /dev/null +++ b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-15 02:53:57.209967", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-15 03:55:15.444151", + "modified_by": "Administrator", + "name": "Stock Settings", + "owner": "Administrator", + "reference_document": "Stock Settings", + "show_full_form": 0, + "title": "Explore Stock Settings", + "validate_action": 1 +} \ No newline at end of file From 41b47a68b36a377d040784b3125e6a2b62d931a9 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 15 May 2020 04:24:36 +0530 Subject: [PATCH 148/410] fix: item price not fetching when customer is unset in item price (#21488) * fix: item price not fetching when customer is unset in item price * fix: item price of selling type has hidden supplier value * fix: remove test variable * fix: test * patch: invalid customer/supplier based on item price type * fix: invalid query * fix: patch Co-authored-by: Marica --- erpnext/patches.txt | 1 + ...stomer_supplier_based_on_type_of_item_price.py | 15 +++++++++++++++ erpnext/stock/doctype/item_price/item_price.py | 7 +++++++ erpnext/stock/get_item_details.py | 2 +- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f72172474cb..0edadcc66df 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -681,3 +681,4 @@ erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) +erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py new file mode 100644 index 00000000000..60aec05466a --- /dev/null +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + invalid_selling_item_price = frappe.db.sql( + """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" + ) + invalid_buying_item_price = frappe.db.sql( + """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" + ) + docs_to_modify = invalid_buying_item_price + invalid_selling_item_price + for d in docs_to_modify: + # saving the doc will auto reset invalid customer/supplier field + doc = frappe.get_doc("Item Price", d[0]) + doc.save() \ No newline at end of file diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 957c41546b3..8e39eb5037d 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -69,3 +69,10 @@ class ItemPrice(Document): self.reference = self.customer if self.buying: self.reference = self.supplier + + if self.selling and not self.buying: + # if only selling then remove supplier + self.supplier = None + if self.buying and not self.selling: + # if only buying then remove customer + self.customer = None diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index d50712aee7d..11b64034198 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -630,7 +630,7 @@ def get_item_price(args, item_code, ignore_party=False): elif args.get("supplier"): conditions += " and supplier=%(supplier)s" else: - conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')" + conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')" if args.get('transaction_date'): conditions += """ and %(transaction_date)s between From 3373dad708faced41801b20a97674a7449ea9e81 Mon Sep 17 00:00:00 2001 From: theopen-institute Date: Fri, 15 May 2020 08:51:39 +0545 Subject: [PATCH 149/410] Added a posting_date field to Fee Schedule, which carries forward to created Fees --- .../education/doctype/fee_schedule/fee_schedule.json | 12 +++++++++++- .../education/doctype/fee_schedule/fee_schedule.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json index 1e98709b2b9..791831810ae 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2017-07-18 15:21:21.527136", @@ -7,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "fee_structure", + "posting_date", "due_date", "naming_series", "fee_creation_status", @@ -259,10 +261,18 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 } ], "is_submittable": 1, - "modified": "2019-05-26 09:10:34.522409", + "links": [], + "modified": "2020-05-15 08:39:20.682837", "modified_by": "Administrator", "module": "Education", "name": "Fee Schedule", diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py index a42800a80d5..1543acdca98 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py @@ -87,6 +87,7 @@ def generate_fee(fee_schedule): } } }) + fees_doc.posting_date = doc.posting_date fees_doc.student = student.student fees_doc.student_name = student.student_name fees_doc.program = student.program From c649468f37db8fb941f940eeae7c63807729840a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 15 May 2020 11:35:41 +0530 Subject: [PATCH 150/410] Fixed typo --- .../v12_0/set_valid_till_date_in_supplier_quotation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py index befa46c31d5..4a6e2288564 100644 --- a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py +++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc("buying", "doctype", "suppplier_quotation") + frappe.reload_doc("buying", "doctype", "supplier_quotation") frappe.db.sql("""UPDATE `tabSupplier Quotation` SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH) - WHERE docstatus < 2""") \ No newline at end of file + WHERE docstatus < 2""") From f984bee5f96723c0bd8d6370c042f7ae4f4f9d47 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 15 May 2020 11:55:42 +0530 Subject: [PATCH 151/410] fix: duplicate leave expiry creation (#21505) * fix: validate existing ledger entries to avoid duplicates * patch: remove duplicate ledger entries created * fix: consider only submitted ledger entries * fix: delete duplicate leaves from the ledger * fix: check if duplicate ledger entry exists * chore: formatting changes Co-authored-by: Nabin Hait --- .../leave_application/leave_application.py | 6 +-- .../leave_ledger_entry/leave_ledger_entry.py | 50 +++++++++++-------- erpnext/patches.txt | 1 + .../remove_duplicate_leave_ledger_entries.py | 44 ++++++++++++++++ 4 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 47b1bb7684e..d2620bec919 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -549,7 +549,7 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return _get_remaining_leaves(total_leaves, allocation.to_date) -def get_leaves_for_period(employee, leave_type, from_date, to_date): +def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False): leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) leave_days = 0 @@ -559,8 +559,8 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): if inclusive_period and leave_entry.transaction_type == 'Leave Encashment': leave_days += leave_entry.leaves - elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \ - and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date): + elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \ + and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)): leave_days += leave_entry.leaves elif leave_entry.transaction_type == 'Leave Application': diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 9ed58c9e59b..63559c4f5ae 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -88,32 +88,40 @@ def get_previous_expiry_ledger_entry(ledger): }, fieldname=['name']) def process_expired_allocation(): - ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry ''' + ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry + Case 1: carry forwarded expiry period is set for the leave type, + create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves + Case 2: leave type has no specific expiry period for carry forwarded leaves + and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves. + ''' # fetch leave type records that has carry forwarded leaves expiry leave_type_records = frappe.db.get_values("Leave Type", filters={ 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - leave_type = [record[0] for record in leave_type_records] + leave_type = [record[0] for record in leave_type_records] or [''] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") - - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + # fetch non expired leave ledger entry of transaction_type allocation + expire_allocation = frappe.db.sql(""" + SELECT + leaves, to_date, employee, leave_type, + is_carry_forward, transaction_name as name, transaction_type + FROM `tabLeave Ledger Entry` l + WHERE (NOT EXISTS + (SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_name = l.transaction_name + AND transaction_type = 'Leave Allocation' + AND name<>l.name + AND docstatus = 1 + AND ( + is_carry_forward=l.is_carry_forward + OR (is_carry_forward = 0 AND leave_type not in %s) + ))) + AND transaction_type = 'Leave Allocation' + AND to_date < %s""", (leave_type, today()), as_dict=1) if expire_allocation: create_expiry_ledger_entry(expire_allocation) @@ -133,6 +141,7 @@ def get_remaining_leaves(allocation): 'employee': allocation.employee, 'leave_type': allocation.leave_type, 'to_date': ('<=', allocation.to_date), + 'docstatus': 1 }, fieldname=['SUM(leaves)']) @frappe.whitelist() @@ -159,7 +168,8 @@ def expire_allocation(allocation, expiry_date=None): def expire_carried_forward_allocation(allocation): ''' Expires remaining leaves in the on carried forward allocation ''' from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period - leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date) + leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, + allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True) leaves = flt(allocation.leaves) + flt(leaves_taken) # allow expired leaves entry to be created diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0edadcc66df..274728151aa 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -678,6 +678,7 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py new file mode 100644 index 00000000000..98a2fcf27ea --- /dev/null +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -0,0 +1,44 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + """Delete duplicate leave ledger entries of type allocation created.""" + if not frappe.db.a_row_exists("Leave Ledger Entry"): + return + + duplicate_records_list = get_duplicate_records() + delete_duplicate_ledger_entries(duplicate_records_list) + +def get_duplicate_records(): + """Fetch all but one duplicate records from the list of expired leave allocation.""" + return frappe.db.sql_list(""" + WITH duplicate_records AS + (SELECT + name, transaction_name, is_carry_forward, + ROW_NUMBER() over(partition by transaction_name order by creation)as row + FROM `tabLeave Ledger Entry` l + WHERE (EXISTS + (SELECT name + FROM `tabLeave Ledger Entry` + WHERE + transaction_name = l.transaction_name + AND transaction_type = 'Leave Allocation' + AND name <> l.name + AND employee = l.employee + AND docstatus = 1 + AND leave_type = l.leave_type + AND is_carry_forward=l.is_carry_forward + AND to_date = l.to_date + AND from_date = l.from_date + AND is_expired = 1 + ))) + SELECT name FROM duplicate_records WHERE row > 1 + """) + +def delete_duplicate_ledger_entries(duplicate_records_list): + """Delete duplicate leave ledger entries.""" + if duplicate_records_list: + frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file From 200af04657edac47a96ef873347c66d93b7d7078 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 15 May 2020 12:10:34 +0530 Subject: [PATCH 152/410] format: better error messages for invalid coupon codes (develop) (#21599) * format: better error messages for invalid coupon codes * fix: remove unnecessary docstatus check --- .../accounts/doctype/pricing_rule/utils.py | 30 +++++++------ erpnext/shopping_cart/cart.py | 44 ++++++++++--------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b358f56671f..cb05481df5a 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -4,13 +4,19 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, copy, json -from frappe import throw, _ + +import copy +import json + from six import string_types -from frappe.utils import flt, cint, get_datetime, get_link_to_form, today + +import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.get_item_details import get_conversion_factor +from frappe import _, throw +from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today + class MultiplePricingRuleConflict(frappe.ValidationError): pass @@ -502,18 +508,16 @@ def get_pricing_rule_items(pr_doc): return list(set(apply_on_data)) def validate_coupon_code(coupon_name): - from frappe.utils import today,getdate - coupon=frappe.get_doc("Coupon Code",coupon_name) + coupon = frappe.get_doc("Coupon Code", coupon_name) + if coupon.valid_from: - if coupon.valid_from > getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has not started")) + if coupon.valid_from > getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has not started")) elif coupon.valid_upto: - if coupon.valid_upto < getdate(today()) : - frappe.throw(_("Sorry,coupon code validity has expired")) - elif coupon.used>=coupon.maximum_use: - frappe.throw(_("Sorry,coupon code are exhausted")) - else: - return + if coupon.valid_upto < getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has expired")) + elif coupon.used >= coupon.maximum_use: + frappe.throw(_("Sorry, this coupon code is no longer valid")) def update_coupon_code_count(coupon_name,transaction_type): coupon=frappe.get_doc("Coupon Code",coupon_name) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index e11e1bb5dcd..4ac546e82c6 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -541,27 +541,31 @@ def show_terms(doc): return doc.tc_name @frappe.whitelist(allow_guest=True) -def apply_coupon_code(applied_code,applied_referral_sales_partner): +def apply_coupon_code(applied_code, applied_referral_sales_partner): quotation = True - if applied_code: - coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name']) - if coupon_list: - coupon_name=coupon_list[0].name - from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code - validate_coupon_code(coupon_name) - quotation = _get_cart_quotation() - quotation.coupon_code=coupon_name + + if not applied_code: + frappe.throw(_("Please enter a coupon code")) + + coupon_list = frappe.get_all('Coupon Code', filters={'coupon_code': applied_code}) + if not coupon_list: + frappe.throw(_("Please enter a valid coupon code")) + + coupon_name = coupon_list[0].name + + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(coupon_name) + quotation = _get_cart_quotation() + quotation.coupon_code = coupon_name + quotation.flags.ignore_permissions = True + quotation.save() + + if applied_referral_sales_partner: + sales_partner_list = frappe.get_all('Sales Partner', filters={'referral_code': applied_referral_sales_partner}) + if sales_partner_list: + sales_partner_name = sales_partner_list[0].name + quotation.referral_sales_partner = sales_partner_name quotation.flags.ignore_permissions = True quotation.save() - if applied_referral_sales_partner: - sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name']) - if sales_partner_list: - sales_partner_name=sales_partner_list[0].name - quotation.referral_sales_partner=sales_partner_name - quotation.flags.ignore_permissions = True - quotation.save() - else: - frappe.throw(_("Please enter valid coupon code !!")) - else: - frappe.throw(_("Please enter coupon code !!")) + return quotation From 7d61c03af41271082bd872c7edbfdb3ac3b478ae Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 15 May 2020 12:58:48 +0530 Subject: [PATCH 153/410] fix: Add missing dimensions in GL entries (#21689) * fix: Add misssing dimensions in GL entries * fix: Add project filter in trial balance report * fix: Use current dimensions instead of dimensions from asset --- erpnext/accounts/deferred_revenue.py | 12 ++++----- .../accounting_dimension.py | 6 ++--- .../invoice_discounting.py | 14 ++++++++--- .../doctype/payment_entry/payment_entry.py | 10 ++++---- .../purchase_invoice/purchase_invoice.py | 13 +++++----- .../doctype/sales_invoice/sales_invoice.py | 25 +++++++++---------- .../report/trial_balance/trial_balance.js | 12 ++++++--- .../report/trial_balance/trial_balance.py | 8 ++++++ erpnext/assets/doctype/asset/asset.py | 12 ++++----- erpnext/education/doctype/fees/fees.py | 6 +++-- .../hr/doctype/expense_claim/expense_claim.py | 13 +++++----- .../expense_claim_detail.json | 17 ++++++++++--- .../expense_taxes_and_charges.json | 17 ++++++++++--- erpnext/patches.txt | 2 +- ...counting_dimensions_in_missing_doctypes.py | 3 ++- 15 files changed, 107 insertions(+), 63 deletions(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index b0210e5fd4b..b57e6783ceb 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -185,7 +185,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): total_days, total_booking_days, account_currency) make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, end_date, project, account_currency, item.cost_center, item.name, deferred_process) + amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) # Returned in case of any errors because it tries to submit the same record again and again in case of errors if frappe.flags.deferred_accounting_error: @@ -222,7 +222,7 @@ def process_deferred_accounting(posting_date=today()): doc.submit() def make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no, deferred_process=None): + amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None): # GL Entry for crediting the amount in the deferred expense from erpnext.accounts.general_ledger import make_gl_entries @@ -236,12 +236,12 @@ def make_gl_entries(doc, credit_account, debit_account, against, "credit": base_amount, "credit_in_account_currency": amount, "cost_center": cost_center, - "voucher_detail_no": voucher_detail_no, + "voucher_detail_no": item.name, 'posting_date': posting_date, 'project': project, 'against_voucher_type': 'Process Deferred Accounting', 'against_voucher': deferred_process - }, account_currency) + }, account_currency, item=item) ) # GL Entry to debit the amount from the expense gl_entries.append( @@ -251,12 +251,12 @@ def make_gl_entries(doc, credit_account, debit_account, against, "debit": base_amount, "debit_in_account_currency": amount, "cost_center": cost_center, - "voucher_detail_no": voucher_detail_no, + "voucher_detail_no": item.name, 'posting_date': posting_date, 'project': project, 'against_voucher_type': 'Process Deferred Accounting', 'against_voucher': deferred_process - }, account_currency) + }, account_currency, item=item) ) if gl_entries: diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 7a85bfb26b6..894ec5bdec5 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -162,9 +162,9 @@ def toggle_disabling(doc): def get_doctypes_with_dimensions(): doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset", - "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item", - "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item", - "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule", + "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", + "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", + "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule", "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription", "Subscription Plan"] diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 39fc203d53f..594b4d4a223 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -8,6 +8,7 @@ from frappe import _ from frappe.utils import flt, getdate, nowdate, add_days from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions class InvoiceDiscounting(AccountsController): def validate(self): @@ -81,10 +82,15 @@ class InvoiceDiscounting(AccountsController): def make_gl_entries(self): company_currency = frappe.get_cached_value('Company', self.company, "default_currency") + gl_entries = [] + invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"] + accounting_dimensions = get_accounting_dimensions() + + invoice_fields.extend(accounting_dimensions) + for d in self.invoices: - inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, - ["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1) + inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1) if d.outstanding_amount: outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate, @@ -102,7 +108,7 @@ class InvoiceDiscounting(AccountsController): "cost_center": inv.cost_center, "against_voucher": d.sales_invoice, "against_voucher_type": "Sales Invoice" - }, inv.party_account_currency)) + }, inv.party_account_currency, item=inv)) gl_entries.append(self.get_gl_dict({ "account": self.accounts_receivable_credit, @@ -115,7 +121,7 @@ class InvoiceDiscounting(AccountsController): "cost_center": inv.cost_center, "against_voucher": d.sales_invoice, "against_voucher_type": "Sales Invoice" - }, ar_credit_account_currency)) + }, ar_credit_account_currency, item=inv)) make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No') diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 83c670eace4..22df5be1b9d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -86,7 +86,7 @@ class PaymentEntry(AccountsController): self.update_payment_schedule(cancel=1) self.set_payment_req_status() self.set_status() - + def set_payment_req_status(self): from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status update_payment_req_status(self, None) @@ -280,7 +280,7 @@ class PaymentEntry(AccountsController): outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]) if outstanding_amount <= 0 and not is_return: no_oustanding_refs.setdefault(d.reference_doctype, []).append(d) - + for k, v in no_oustanding_refs.items(): frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.

    \ If this is undesirable please cancel the corresponding Payment Entry.") @@ -506,7 +506,7 @@ class PaymentEntry(AccountsController): "against": against_account, "account_currency": self.party_account_currency, "cost_center": self.cost_center - }) + }, item=self) dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit" @@ -550,7 +550,7 @@ class PaymentEntry(AccountsController): "credit_in_account_currency": self.paid_amount, "credit": self.base_paid_amount, "cost_center": self.cost_center - }) + }, item=self) ) if self.payment_type in ("Receive", "Internal Transfer"): gl_entries.append( @@ -561,7 +561,7 @@ class PaymentEntry(AccountsController): "debit_in_account_currency": self.received_amount, "debit": self.base_received_amount, "cost_center": self.cost_center - }) + }, item=self) ) def add_deductions_gl_entries(self, gl_entries): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3aa24df16d3..cf4e158bbaa 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -460,7 +460,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) def make_item_gl_entries(self, gl_entries): @@ -841,7 +841,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( @@ -852,7 +852,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": self.base_paid_amount \ if bank_account_currency==self.company_currency else self.paid_amount, "cost_center": self.cost_center - }, bank_account_currency) + }, bank_account_currency, item=self) ) def make_write_off_gl_entry(self, gl_entries): @@ -873,7 +873,7 @@ class PurchaseInvoice(BuyingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -883,7 +883,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": self.base_write_off_amount \ if write_off_account_currency==self.company_currency else self.write_off_amount, "cost_center": self.cost_center or self.write_off_cost_center - }) + }, item=self) ) def make_gle_for_rounding_adjustment(self, gl_entries): @@ -902,8 +902,7 @@ class PurchaseInvoice(BuyingController): "debit_in_account_currency": self.rounding_adjustment, "debit": self.base_rounding_adjustment, "cost_center": self.cost_center or round_off_cost_center, - } - )) + }, item=self)) def on_cancel(self): super(PurchaseInvoice, self).on_cancel() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 3b0fade0e53..05b85dabd4f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -791,7 +791,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) def make_tax_gl_entries(self, gl_entries): @@ -808,7 +808,7 @@ class SalesInvoice(SellingController): tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))), "cost_center": tax.cost_center - }, account_currency) + }, account_currency, item=tax) ) def make_item_gl_entries(self, gl_entries): @@ -828,7 +828,7 @@ class SalesInvoice(SellingController): for gle in fixed_asset_gl_entries: gle["against"] = self.customer - gl_entries.append(self.get_gl_dict(gle)) + gl_entries.append(self.get_gl_dict(gle, item=item)) asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) @@ -866,7 +866,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }) + }, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -875,7 +875,7 @@ class SalesInvoice(SellingController): "against": self.customer, "debit": self.loyalty_amount, "remark": "Loyalty Points redeemed by the customer" - }) + }, item=self) ) def make_pos_gl_entries(self, gl_entries): @@ -896,7 +896,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) payment_mode_account_currency = get_account_currency(payment_mode.account) @@ -909,7 +909,7 @@ class SalesInvoice(SellingController): if payment_mode_account_currency==self.company_currency \ else payment_mode.amount, "cost_center": self.cost_center - }, payment_mode_account_currency) + }, payment_mode_account_currency, item=self) ) def make_gle_for_change_amount(self, gl_entries): @@ -927,7 +927,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( @@ -936,7 +936,7 @@ class SalesInvoice(SellingController): "against": self.customer, "credit": self.base_change_amount, "cost_center": self.cost_center - }) + }, item=self) ) else: frappe.throw(_("Select change amount account"), title="Mandatory Field") @@ -960,7 +960,7 @@ class SalesInvoice(SellingController): "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center - }, self.party_account_currency) + }, self.party_account_currency, item=self) ) gl_entries.append( self.get_gl_dict({ @@ -971,7 +971,7 @@ class SalesInvoice(SellingController): self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency else flt(self.write_off_amount, self.precision("write_off_amount"))), "cost_center": self.cost_center or self.write_off_cost_center or default_cost_center - }, write_off_account_currency) + }, write_off_account_currency, item=self) ) def make_gle_for_rounding_adjustment(self, gl_entries): @@ -988,8 +988,7 @@ class SalesInvoice(SellingController): "credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")), "cost_center": self.cost_center or round_off_cost_center, - } - )) + }, item=self)) def update_billing_status_in_dn(self, update_modified=True): updated_delivery_notes = [] diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 622bab6946f..07752e1e626 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -46,7 +46,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": frappe.defaults.get_user_default("year_end_date"), }, { - "fieldname":"cost_center", + "fieldname": "cost_center", "label": __("Cost Center"), "fieldtype": "Link", "options": "Cost Center", @@ -61,7 +61,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }, { - "fieldname":"finance_book", + "fieldname": "project", + "label": __("Project"), + "fieldtype": "Link", + "options": "Project" + }, + { + "fieldname": "finance_book", "label": __("Finance Book"), "fieldtype": "Link", "options": "Finance Book", @@ -97,7 +103,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ + frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ "fieldname": dimension["fieldname"], "label": __(dimension["label"]), "fieldtype": "Link", diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index d78324157a9..8bd4399e608 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -69,6 +69,10 @@ def get_data(filters): gl_entries_by_account = {} opening_balances = get_opening_balances(filters) + + #add filter inside list so that the query in financial_statements.py doesn't break + filters.project = [filters.project] + set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) @@ -102,6 +106,9 @@ def get_rootwise_opening_balances(filters, report_type): additional_conditions += """ and cost_center in (select name from `tabCost Center` where lft >= %s and rgt <= %s)""" % (lft, rgt) + if filters.project: + additional_conditions += " and project = %(project)s" + if filters.finance_book: fb_conditions = " AND finance_book = %(finance_book)s" if filters.include_default_book_entries: @@ -116,6 +123,7 @@ def get_rootwise_opening_balances(filters, report_type): "from_date": filters.from_date, "report_type": report_type, "year_start_date": filters.year_start_date, + "project": filters.project, "finance_book": filters.finance_book, "company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book') } diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a3200d56443..505ba4c6b64 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -125,7 +125,7 @@ class Asset(AccountsController): if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date): frappe.throw(_("Available-for-use Date should be after purchase date")) - + def validate_gross_and_purchase_amount(self): if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ @@ -455,7 +455,7 @@ class Asset(AccountsController): for d in self.get('finance_books'): if d.finance_book == self.default_finance_book: return cint(d.idx) - 1 - + def validate_make_gl_entry(self): purchase_document = self.get_purchase_document() asset_bought_with_invoice = purchase_document == self.purchase_invoice @@ -487,14 +487,14 @@ class Asset(AccountsController): purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt return purchase_document - + def get_asset_accounts(self): fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name, asset_category = self.asset_category, company = self.company) cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company) - + return fixed_asset_account, cwip_account def make_gl_entries(self): @@ -513,7 +513,7 @@ class Asset(AccountsController): "credit": self.purchase_receipt_amount, "credit_in_account_currency": self.purchase_receipt_amount, "cost_center": self.cost_center - })) + }, item=self)) gl_entries.append(self.get_gl_dict({ "account": fixed_asset_account, @@ -523,7 +523,7 @@ class Asset(AccountsController): "debit": self.purchase_receipt_amount, "debit_in_account_currency": self.purchase_receipt_amount, "cost_center": self.cost_center - })) + }, item=self)) if gl_entries: from erpnext.accounts.general_ledger import make_gl_entries diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py index f0d60faed6f..25d67d2d5f6 100644 --- a/erpnext/education/doctype/fees/fees.py +++ b/erpnext/education/doctype/fees/fees.py @@ -98,14 +98,16 @@ class Fees(AccountsController): "debit_in_account_currency": self.grand_total, "against_voucher": self.name, "against_voucher_type": self.doctype - }) + }, item=self) + fee_gl_entry = self.get_gl_dict({ "account": self.income_account, "against": self.student, "credit": self.grand_total, "credit_in_account_currency": self.grand_total, "cost_center": self.cost_center - }) + }, item=self) + from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2), update_outstanding="Yes", merge_entries=False) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index ac1bfa1a391..ea469b82c98 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -116,8 +116,9 @@ class ExpenseClaim(AccountsController): "party_type": "Employee", "party": self.employee, "against_voucher_type": self.doctype, - "against_voucher": self.name - }) + "against_voucher": self.name, + "cost_center": self.cost_center + }, item=self) ) # expense entries @@ -129,7 +130,7 @@ class ExpenseClaim(AccountsController): "debit_in_account_currency": data.sanctioned_amount, "against": self.employee, "cost_center": data.cost_center - }) + }, item=data) ) for data in self.advances: @@ -157,7 +158,7 @@ class ExpenseClaim(AccountsController): "credit": self.grand_total, "credit_in_account_currency": self.grand_total, "against": self.employee - }) + }, item=self) ) gl_entry.append( @@ -170,7 +171,7 @@ class ExpenseClaim(AccountsController): "debit_in_account_currency": self.grand_total, "against_voucher": self.name, "against_voucher_type": self.doctype, - }) + }, item=self) ) return gl_entry @@ -187,7 +188,7 @@ class ExpenseClaim(AccountsController): "cost_center": self.cost_center, "against_voucher_type": self.doctype, "against_voucher": self.name - }) + }, item=tax) ) def validate_account_details(self): diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json index 16e9eef9174..3cce50e0905 100644 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json +++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json @@ -13,9 +13,11 @@ "description", "section_break_6", "amount", - "cost_center", "column_break_8", - "sanctioned_amount" + "sanctioned_amount", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" ], "fields": [ { @@ -104,12 +106,21 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2019-12-11 13:42:33.233432", + "modified": "2020-05-11 18:54:35.601592", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim Detail", 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 d68caf1cc1b..885e3eed976 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 @@ -8,14 +8,16 @@ "engine": "InnoDB", "field_order": [ "account_head", - "cost_center", "rate", "col_break1", "description", "section_break_6", "tax_amount", "column_break_8", - "total" + "total", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" ], "fields": [ { @@ -91,11 +93,20 @@ { "fieldname": "column_break_8", "fieldtype": "Column Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2020-03-11 13:25:06.721917", + "modified": "2020-05-11 19:01:26.611758", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 274728151aa..e7df4722723 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -623,7 +623,7 @@ erpnext.patches.v11_1.update_default_supplier_in_item_defaults erpnext.patches.v12_0.update_due_date_in_gle erpnext.patches.v12_0.add_default_buying_selling_terms_in_company erpnext.patches.v12_0.update_ewaybill_field_position -erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes +erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes #2020-05-11 erpnext.patches.v11_1.set_status_for_material_request_type_manufacture erpnext.patches.v12_0.move_plaid_settings_to_doctype execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link') diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py index b71ea665943..657decfed23 100644 --- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py +++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py @@ -20,7 +20,8 @@ def execute(): else: insert_after_field = 'accounting_dimensions_section' - for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item"]: + for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", + "Expense Claim Detail", "Expense Taxes and Charges"]: field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) From be5c45a9ae1fc0ddc48de5107e624a2ba144f63c Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 13:21:58 +0530 Subject: [PATCH 154/410] chore: Fixtures and empty state handling in chart widgets --- .../warehouse_wise_stock_value.py | 4 + erpnext/stock/dashboard_fixtures.py | 207 ++++++++++++++++++ .../delivery_note_trends.py | 3 + .../item_shortage_report.py | 2 +- .../purchase_receipt_trends.py | 3 + .../stock/report/stock_ageing/stock_ageing.py | 3 + 6 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 erpnext/stock/dashboard_fixtures.py diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index da2f9350c6d..05a50687524 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -25,6 +25,10 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d wh["balance"] = balance[0][0] warehouses = [x for x in warehouses if not (x.get('balance') == None)] + + if not warehouses: + return [] + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) if len(sorted_warehouse_map) > 10: diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py new file mode 100644 index 00000000000..db955c39c95 --- /dev/null +++ b/erpnext/stock/dashboard_fixtures.py @@ -0,0 +1,207 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1).get("name") + +def get_dashboards(): + return [{ + "name": "Stock", + "dashboard_name": "Stock", + "charts": [ + { "chart": "Item Shortage Summary", "width": "Half"}, + { "chart": "Stock Ageing", "width": "Half"}, + { "chart": "Item Wise Annual Revenue", "width": "Half"}, + { "chart": "Item Wise Annual Expenditure", "width": "Half"}, + { "chart": "Warehouse wise Stock Value", "width": "Full"} + ], + "cards": [ + { "card": "Purchase Receipts to Bill"}, + { "card": "Amount Payable against Receipt"}, + { "card": "Delivery Notes to Bill"}, + { "card": "Amount Receivable against Delivery"} + ] + }] + +def get_charts(): + return [ + { + "name": "Item Shortage Summary", + "chart_name": "Item Shortage Summary", + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Item Shortage Report", + "type": "Bar" + }, + { + "name": "Stock Ageing", + "chart_name": "Stock Ageing", + "chart_type": "Report", + "custom_options": json.dumps({ + "colors": ["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "to_date": nowdate(), + "show_warehouse_wise_stock": 0 + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Stock Ageing", + "type": "Bar" + }, + { + "name": "Item Wise Annual Revenue", + "chart_name": "Item Wise Annual Revenue", + "chart_type": "Report", + "custom_options": json.dumps({ + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "colors":["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "period": "Monthly", + "based_on": "Item", + "fiscal_year": fiscal_year, + "company": company.name + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Delivery Note Trends", + "type": "Bar" + }, + { + "name": "Item Wise Annual Expenditure", + "chart_name": "Item Wise Annual Expenditure", + "chart_type": "Report", + "custom_options": json.dumps({ + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "colors":["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "period": "Monthly", + "based_on": "Item", + "fiscal_year": fiscal_year, + "company": company.name, + "period_based_on": "posting_date" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "type": "Bar" + }, + { + "name": "Warehouse wise Stock Value", + "chart_name": "Warehouse wise Stock Value", + "chart_type": "Custom", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({}), + "is_custom": 0, + "is_public": 1, + "owner": "Administrator", + "source": "Warehouse wise Stock Value", + "type": "Bar" + } + + ] + +def get_number_cards(): + return [ + { + "name": "Amount Payable against Receipt", + "label": "Amount Payable against Receipt", + "function": "Sum", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Purchase Receipt", + "filters_json": json.dumps( + [["Purchase Receipt","status","=","To Bill",False], + ["Purchase Receipt","company","=", company.name, False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Amount Receivable against Delivery", + "label": "Amount Receivable against Delivery", + "function": "Sum", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Delivery Note", + "filters_json": json.dumps( + [["Delivery Note","company","=",company.name,False], + ["Delivery Note","status","=","To Bill",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Purchase Receipts to Bill", + "label": "Purchase Receipts to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Purchase Receipt", + "filters_json": json.dumps( + [["Purchase Receipt","status","=","To Bill",False], + ["Purchase Receipt","company","=", company.name, False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Delivery Notes to Bill", + "label": "Delivery Notes to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Delivery Note", + "filters_json": json.dumps( + [["Delivery Note","company","=",company.name,False], + ["Delivery Note","status","=","To Bill",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } + ] \ No newline at end of file diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 2108b51afc8..d088b0020d8 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -17,6 +17,9 @@ def execute(filters=None): return conditions["columns"], data, None, chart_data def get_chart_data(data): + if not data: + return [] + labels, datapoints = [], [] if len(data) > 10: diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py index 07749ebec57..086d833bbc4 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -11,7 +11,7 @@ def execute(filters=None): data = get_data(conditions, filters) if not data: - return [], [] + return [], [], None, [] chart_data = get_chart_data(data) diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index ac235f74e1c..627c23b1051 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -17,6 +17,9 @@ def execute(filters=None): return conditions["columns"], data, None, chart_data def get_chart_data(data): + if not data: + return [] + labels, datapoints = [], [] if len(data) > 10: diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 48f22c2c24d..c5b8f43f968 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -234,6 +234,9 @@ def get_sle_conditions(filters): return "and {}".format(" and ".join(conditions)) if conditions else "" def get_chart_data(data, filters): + if not data: + return [] + labels, datapoints = [], [] if filters.get("show_warehouse_wise_stock"): From 940856cc16bca544963473afea0d42644de2cf01 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 15 May 2020 14:30:24 +0530 Subject: [PATCH 155/410] fix: add tests for set_status --- .../doctype/purchase_invoice/purchase_invoice.py | 2 +- .../purchase_invoice/test_purchase_invoice.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 265969db1f0..baf2ba9fd41 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1042,7 +1042,7 @@ class PurchaseInvoice(BuyingController): elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" #Check if outstanding amount is 0 due to debit 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}): + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Debit Note Issued" elif self.is_return == 1: self.status = "Return" diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e41ad428469..61700050614 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -86,6 +86,8 @@ class TestPurchaseInvoice(unittest.TestCase): pe.submit() pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name) + pi_doc.load_from_db() + self.assertTrue(pi_doc.status, "Paid") self.assertRaises(frappe.LinkExistsError, pi_doc.cancel) unlink_payment_on_cancel_of_invoice() @@ -203,7 +205,9 @@ class TestPurchaseInvoice(unittest.TestCase): pi.insert() pi.submit() + pi.load_from_db() + self.assertTrue(pi.status, "Unpaid") self.check_gle_for_pi(pi.name) def check_gle_for_pi(self, pi): @@ -234,6 +238,9 @@ class TestPurchaseInvoice(unittest.TestCase): pi = frappe.copy_doc(test_records[0]) pi.insert() + pi.load_from_db() + + self.assertTrue(pi.status, "Draft") pi.naming_series = 'TEST-' self.assertRaises(frappe.CannotChangeConstantError, pi.save) @@ -248,6 +255,8 @@ class TestPurchaseInvoice(unittest.TestCase): pi.get("taxes").pop(1) pi.insert() pi.submit() + pi.load_from_db() + self.assertTrue(pi.status, "Unpaid") gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s @@ -599,6 +608,11 @@ class TestPurchaseInvoice(unittest.TestCase): # return entry pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1) + pi.load_from_db() + self.assertTrue(pi.status, "Debit Note Issued") + pi1.load_from_db() + self.assertTrue(pi1.status, "Return") + actual_qty_2 = get_qty_after_transaction() self.assertEqual(actual_qty_1 - 2, actual_qty_2) @@ -771,6 +785,8 @@ class TestPurchaseInvoice(unittest.TestCase): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1) + pi.load_from_db() + self.assertTrue(pi.status, "Return") outstanding_amount = get_outstanding_amount(pi.doctype, pi.name, "Creditors - _TC", pi.supplier, "Supplier") From ec1f9594fa452059231f7aa87c76a5e563edd58d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 15:17:11 +0530 Subject: [PATCH 156/410] fix: Refined Onboarding, desk cards and renamed field in Buying Settings --- erpnext/buying/desk_page/buying/buying.json | 43 ++++++--------- .../buying_settings/buying_settings.js | 23 ++++++++ .../buying_settings/buying_settings.json | 13 +++-- .../module_onboarding/buying/buying.json | 54 +++++++++++++++++++ .../buying_settings/buying_settings.json | 11 ++-- .../create_a_material_request.json | 19 +++++++ .../create_a_product/create_a_product.json | 5 +- .../create_a_supplier/create_a_supplier.json | 5 +- .../create_a_warehouse.json | 7 ++- .../create_your_first_purchase_order.json | 5 +- .../introduction_to_buying.json | 3 ++ 11 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 erpnext/buying/module_onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 9749f3c97fc..e00db0e86d7 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" } ], "cards_label": "Masters & Reports ", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-06 18:10:12.760321", + "modified": "2020-05-15 14:26:42.505702", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -64,11 +64,11 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", - "format": "{} to Receive", - "label": "Purchase Order", - "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", + "color": "#cef6d1", + "format": "{} available", + "label": "Item", + "link_to": "Item", + "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, { @@ -81,31 +81,20 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill ", - "label": "Purchase Receipt", - "link_to": "Purchase Receipt", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "format": "{} to Receive", + "label": "Purchase Order", + "link_to": "Purchase Order", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { - "color": "#ffe8cd", - "format": "{} Unpaid / Overdue", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", - "type": "DocType" + "label": "Purchase Analytics", + "link_to": "Purchase Analytics", + "type": "Report" }, { - "color": "#cef6d1", - "format": "{} Active", - "label": "Supplier Quotation", - "link_to": "Supplier Quotation", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", - "type": "DocType" - }, - { - "label": "Item-wise Purchase Register", - "link_to": "Item-wise Purchase Register", + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", "type": "Report" } ], diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 403b1c95e74..5a5afa41fcd 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -6,3 +6,26 @@ frappe.ui.form.on('Buying Settings', { // } }); + +frappe.tour['Buying Settings'] = [ + { + fieldname: "supp_master_name", + title: "Supplier Naming By", + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + }, + { + fieldname: "buying_price_list", + title: "Default Buying Price List", + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + }, + { + fieldname: "po_required", + title: "Purchase Order Required for Purchase Invoice & Receipt Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") + }, + { + fieldname: "pr_required", + title: "Purchase Receipt Required for Purchase Invoice Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") + } +]; \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index a492519591b..a0ab2a00f99 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-06-25 11:04:03", "description": "Settings for Buying Module", "doctype": "DocType", "document_type": "Other", + "engine": "InnoDB", "field_order": [ "supp_master_name", "supplier_group", @@ -44,13 +46,13 @@ { "fieldname": "po_required", "fieldtype": "Select", - "label": "Purchase Order Required", + "label": "Purchase Order Required for Purchase Invoice & Receipt Creation", "options": "No\nYes" }, { "fieldname": "pr_required", "fieldtype": "Select", - "label": "Purchase Receipt Required", + "label": "Purchase Receipt Required for Purchase Invoice Creation", "options": "No\nYes" }, { @@ -92,7 +94,8 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-08-20 13:13:09.055189", + "links": [], + "modified": "2020-05-15 14:49:32.513611", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -107,5 +110,7 @@ "share": 1, "write": 1 } - ] + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json new file mode 100644 index 00000000000..fc956baaa3b --- /dev/null +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-15 14:39:16.514904", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" + }, + { + "step": "Create a Material Request" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 3b3208f5f04..45a19fb4a18 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,16 +1,19 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, + "is_single": 1, "is_skipped": 0, - "modified": "2020-05-12 18:30:06.323797", + "modified": "2020-05-15 14:38:01.142256", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", - "title": "Configure Buying Settings." + "show_full_form": 0, + "title": "Explore Buying Settings.", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json new file mode 100644 index 00000000000..9dc493dd499 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 14:39:09.818764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 14:39:09.818764", + "modified_by": "Administrator", + "name": "Create a Material Request", + "owner": "Administrator", + "reference_document": "Material Request", + "show_full_form": 1, + "title": "Create a Material Request", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json index dce1a215053..d2068e167b7 100644 --- a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:30:02.489949", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", "reference_document": "Item", - "title": "Create a Product" + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index f87a43dbc39..7bbbc928418 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", "reference_document": "Supplier", - "title": "Create a Supplier" + "show_full_form": 0, + "title": "Create a Supplier", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json index 20744f6624b..8aac6d4cf48 100644 --- a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:27:44.128737", + "modified": "2020-05-15 14:32:17.072731", "modified_by": "Administrator", "name": "Create a Warehouse", "owner": "Administrator", "reference_document": "Warehouse", - "title": "Setup your Purchase Warehouse" + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json index 0efadbd7887..9dbed239789 100644 --- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:31:56.856112", "modified_by": "Administrator", "name": "Create your first Purchase Order", "owner": "Administrator", "reference_document": "Purchase Order", - "title": "Create your first Purchase Order" + "show_full_form": 0, + "title": "Create your first Purchase Order", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index 73f22df48c1..fd98fddafae 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to Buying", + "validate_action": 1, "video_url": "https://youtu.be/efFajTTQBa8" } \ No newline at end of file From 03d165ff79291a3b4c104f398277fbbd43d8cb84 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 15 May 2020 16:39:02 +0530 Subject: [PATCH 157/410] fix: update remark on submitting payment entry --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 83c670eace4..7e3b1349f20 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -451,8 +451,6 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): - if self.remarks: return - if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] From 7be71c88f21b93f9058a1dbe96fa79509a05ccac Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 15 May 2020 19:23:41 +0530 Subject: [PATCH 158/410] fix: Better validation message for group accounts (#21725) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index efab5801e8b..291aff3f5ad 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -112,8 +112,8 @@ class GLEntry(Document): from tabAccount where name=%s""", self.account, as_dict=1)[0] if ret.is_group==1: - frappe.throw(_("{0} {1}: Account {2} cannot be a Group") - .format(self.voucher_type, self.voucher_no, self.account)) + frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in + transactions''').format(self.voucher_type, self.voucher_no, self.account)) if ret.docstatus==2: frappe.throw(_("{0} {1}: Account {2} is inactive") From 5cfbdf4bcbb96ec891f114e891e42d6948dfb694 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 15 May 2020 19:36:23 +0530 Subject: [PATCH 159/410] fix: user not able to view product (#21740) --- erpnext/shopping_cart/product_info.py | 8 +++++--- erpnext/stock/doctype/item/item.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index a7da09cb808..21ee335125b 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -10,14 +10,16 @@ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @frappe.whitelist(allow_guest=True) -def get_product_info_for_website(item_code): +def get_product_info_for_website(item_code, skip_quotation_creation=False): """get product price / stock info for website""" cart_settings = get_shopping_cart_settings() if not cart_settings.enabled: return frappe._dict() - cart_quotation = _get_cart_quotation() + cart_quotation = frappe._dict() + if not skip_quotation_creation: + cart_quotation = _get_cart_quotation() price = get_price( item_code, @@ -51,7 +53,7 @@ def get_product_info_for_website(item_code): def set_product_info_for_website(item): """set product price uom for website""" - product_info = get_product_info_for_website(item.item_code) + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True) if product_info: item.update(product_info) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 4cc50bba9ea..7a1c1279eae 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -467,7 +467,7 @@ class Item(WebsiteGenerator): def set_shopping_cart_data(self, context): from erpnext.shopping_cart.product_info import get_product_info_for_website - context.shopping_cart = get_product_info_for_website(self.name) + context.shopping_cart = get_product_info_for_website(self.name, skip_quotation_creation=True) def add_default_uom_in_conversion_factor_table(self): uom_conv_list = [d.uom for d in self.get("uoms")] From a74d433cd81661120b4bcb5f9099ce2d3b25fbde Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:37:48 +0530 Subject: [PATCH 160/410] fix: Fixed Dashboard Charts, modified fixtures and minor changes --- erpnext/buying/dashboard_fixtures.py | 217 ++++++++++++------ .../buying_settings/buying_settings.js | 2 +- .../supplier_quotation.json | 4 +- .../purchase_order_analysis.js | 8 - .../purchase_order_analysis.json | 4 +- .../purchase_order_analysis.py | 13 +- .../purchase_order_trends.py | 43 +++- erpnext/public/js/purchase_trends_filters.js | 5 +- 8 files changed, 198 insertions(+), 98 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 0bd9a1fd488..291abb87b04 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -3,7 +3,8 @@ import frappe import json - +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ @@ -23,108 +24,172 @@ def get_company_for_dashboards(): return None company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) def get_dashboards(): return [{ "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Purchase Analytics", "width": "Full"}, - { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Requested Items to Order", "width": "Full"} + { "chart": "Purchase Order Trends", "width": "Full"} + ], + "cards": [ + { "card": "Purchase Orders to Receive"}, + { "card": "Purchase Order Expenses"}, + { "card": "Active Suppliers"}, + { "card": "Active Supplier Quotations"} ] }] def get_charts(): return [ - { - "name": "Purchase Analytics", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "x_field": "entity", - "type": "Bar", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "name": "Material Request Purchase Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, { "name": "Purchase Order Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", "chart_name": "Purchase Order Analysis", + "chart_type": "Report", "custom_options": json.dumps({ "type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Requested Items to Order", + }), "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", "filters_json": json.dumps({ "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 + "from_date": start_date, + "to_date": end_date }), "is_custom": 1, - "type": "Bar", - "timeseries": 0, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "type": "Donut" + }, + { + "name": "Material Request Analysis", + "chart_name": "Material Request Analysis", + "chart_type": "Group By", + "custom_options": json.dumps({"height": 300}), + "doctype": "Dashboard Chart", + "document_type": "Material Request", + "filters_json": json.dumps( + [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], + ["Material Request", "material_request_type", "=", "Purchase", False], + ["Material Request", "company", "=", company.name, False]] + ), + "group_by_based_on": "status", + "group_by_type": "Count", + "is_custom": 0, + "is_public": 1, + "number_of_groups": 0, + "owner": "Administrator", + "type": "Donut" + }, + { + "name": "Purchase Order Trends", + "chart_name": "Purchase Order Trends", "chart_type": "Report", - "chart_name": "Requested Items to Order", "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) + "type": "line", + "regionFill": 1, + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Item" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Trends", + "type": "Line" + }, + { + "name": "Top Suppliers", + "chart_name": "Top Suppliers", + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Supplier" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "type": "Bar" } ] def get_number_cards(): - return [{}] \ No newline at end of file + return [ + { + "name": "Purchase Order Expenses", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], + ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Sum", + "is_public": 1, + "label": "Purchase Order Expenses", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Purchase Orders to Receive", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Count", + "is_public": 1, + "label": "Purchase Orders to Receive", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Active Suppliers", + "doctype": "Number Card", + "document_type": "Supplier", + "filters_json": json.dumps([["Supplier", "disabled", "=", "0"]]), + "function": "Count", + "is_public": 1, + "label": "Active Suppliers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Active Supplier Quotations", + "doctype": "Number Card", + "document_type": "Supplier Quotation", + "filters_json": json.dumps([["Supplier Quotation", "status", "=", "Submitted", False]]), + "function": "Count", + "is_public": 1, + "label": "Active Supplier Quotations", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + } + ] \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 5a5afa41fcd..a27950a9414 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -16,7 +16,7 @@ frappe.tour['Buying Settings'] = [ { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") }, { fieldname: "po_required", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 3bc441af6d1..7db1516ce1b 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -761,7 +761,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled", + "options": "\nDraft\nSubmitted\nStopped\nCancelled\nExpired", "print_hide": 1, "read_only": 1, "reqd": 1, @@ -803,7 +803,7 @@ "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-04-15 11:44:52.958022", + "modified": "2020-05-15 21:24:12.639482", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 24abb6d44ab..701da4380aa 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -58,14 +58,6 @@ frappe.query_reports["Purchase Order Analysis"] = { return options } }, - { - "fieldname": "chart_based_on", - "label": __("Chart Based On"), - "fieldtype": "Select", - "width": "80", - "options": "Quantity\nAmount", - "default": "Quantity" - }, { "fieldname": "group_by_po", "label": __("Group by Purchase Order"), diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json index 196aaaed224..5ba3101ec51 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2020-05-04 18:41:28.625119", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-05-04 18:41:28.625119", + "modified": "2020-05-15 20:57:52.623455", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Analysis", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 96e2fc8a209..497ce684fd6 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -82,10 +82,9 @@ def get_data(conditions, filters): return data def prepare_data(data, filters): - completed, pending = 0,0 - chart_based_on = filters.get("chart_based_on") - pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" - completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + completed, pending = 0, 0 + pending_field = "pending_amount" + completed_field = "billed_amount" if filters.get("group_by_po"): purchase_order_map = {} @@ -116,7 +115,7 @@ def prepare_data(data, filters): for field in fields: po_row[field] = flt(row[field]) + flt(po_row[field]) - chart_data = prepare_chart_data(chart_based_on, pending, completed) + chart_data = prepare_chart_data(pending, completed) if filters.get("group_by_po"): data = [] @@ -126,8 +125,8 @@ def prepare_data(data, filters): return data, chart_data -def prepare_chart_data(chart_based_on, pending, completed): - labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] +def prepare_chart_data(pending, completed): + labels = ["Amount to Bill", "Billed Amount"] return { "data" : { diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 888676cf64f..011760dacc3 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,5 +11,45 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Purchase Order") data = get_data(filters, conditions) + chart_data = get_chart_data(data, conditions, filters) - return conditions["columns"], data \ No newline at end of file + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Supplier"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "values" : datapoints + } + ] + }, + "type" : "line" + } \ No newline at end of file diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index cd767f5d167..c786a8674e6 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -51,7 +51,10 @@ erpnext.get_purchase_trends_filters = function() { { "value": "Supplier Group", "label": __("Supplier Group") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1 + } }, { "fieldname":"group_by", From 75c65fd1b1eb6ee79858ad75f3bd5a4aa8eec6bb Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:47:51 +0530 Subject: [PATCH 161/410] fix: Handle empty chart state and change desk dashboard --- erpnext/buying/desk_page/buying/buying.json | 6 +++--- erpnext/buying/module_onboarding/buying/buying.json | 2 +- .../purchase_order_analysis/purchase_order_analysis.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index e00db0e86d7..1a69f422695 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -40,8 +40,8 @@ "category": "Modules", "charts": [ { - "chart_name": "Purchase Analytics", - "label": "Buying Analytics" + "chart_name": "Purchase Order Trends", + "label": "Purchase Order Trends" } ], "charts_label": "Buying Dashboard", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-15 14:26:42.505702", + "modified": "2020-05-15 23:41:09.307288", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index fc956baaa3b..70d6cbb77a4 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 14:39:16.514904", + "modified": "2020-05-15 23:42:35.625736", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 497ce684fd6..89be62231b9 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -19,7 +19,7 @@ def execute(filters=None): data = get_data(conditions, filters) if not data: - return [], [] + return [], [], None, [] data, chart_data = prepare_data(data, filters) From 49dbbdc4cb2a7b8c385cca7b92684c7ab6c5e606 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 16 May 2020 04:59:43 +0530 Subject: [PATCH 162/410] fix: promotional scheme not able to savce --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 19f571fb306..4d9053a55b3 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -99,7 +99,7 @@ class PricingRule(Document): self.same_item = 1 def validate_max_discount(self): - if self.rate_or_discount == "Discount Percentage" and self.items: + if self.rate_or_discount == "Discount Percentage" and self.get("items"): for d in self.items: max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount") if max_discount and flt(self.discount_percentage) > flt(max_discount): From 2a0e3e3515ed4b25cbbe771ec39d5472aa9d630e Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 16 May 2020 18:07:03 +0530 Subject: [PATCH 163/410] fix: Remove strip --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index e904a681f66..93bec686cec 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,7 +58,7 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name.strip() if tax_withholding.category_name.strip() else tax_withholding_category + "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category }) def get_tax_withholding_rates(tax_withholding, fiscal_year): From fc514ba940f4017fdf569ee558f076571143a9de Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:00:00 +0530 Subject: [PATCH 164/410] feat: save contact to woocommerce --- .../connectors/woocommerce_connection.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 54fa6085d03..e4593a02b9f 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -78,11 +78,39 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name else: billing_address = create_address(raw_billing_data, customer, "Billing") shipping_address = create_address(raw_shipping_data, customer, "Shipping") + contact = create_contact(raw_billing_data, customer) if customer_exists: rename_address(billing_address, customer) rename_address(shipping_address, customer) +def create_contact(data, customer): + email = data.get("email", None) + phone = data.get("phone", None) + + if not email and not phone: + return + + contact = frappe.new_doc("Contact") + contact.first_name = data.get("first_name") + contact.last_name = data.get("last_name") + contact.is_primary_contact = 1 + contact.is_billing_contact = 1 + + if phone: + contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1) + + if email: + contact.add_email(email, is_primary=1) + + contact.append("links", { + "link_doctype": "Customer", + "link_name": customer.customer_name + }) + + contact.flags.ignore_mandatory = True + contact.save() + def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -102,7 +130,7 @@ def create_address(raw_data, customer, address_type): }) address.flags.ignore_mandatory = True - address = address.save() + address.save() def rename_address(address, customer): old_address_title = address.name From 180bda76186a65ab68c70abd06d66d1f586d0f6a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:20:55 +0530 Subject: [PATCH 165/410] refactor: return address and contact objects --- .../erpnext_integrations/connectors/woocommerce_connection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index e4593a02b9f..1e422db828b 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -111,6 +111,8 @@ def create_contact(data, customer): contact.flags.ignore_mandatory = True contact.save() + return contact + def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -132,6 +134,8 @@ def create_address(raw_data, customer, address_type): address.flags.ignore_mandatory = True address.save() + return address + def rename_address(address, customer): old_address_title = address.name new_address_title = customer.customer_name + "-" + address.address_type From d69e3eb5d79bd3276c83713c25d88e35c38f038f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:37:03 +0530 Subject: [PATCH 166/410] feat: use name instead of customer name to link address and contact --- .../connectors/woocommerce_connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1e422db828b..6d379f6ed5f 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -105,7 +105,7 @@ def create_contact(data, customer): contact.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) contact.flags.ignore_mandatory = True @@ -128,7 +128,7 @@ def create_address(raw_data, customer, address_type): address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) address.flags.ignore_mandatory = True @@ -138,7 +138,7 @@ def create_address(raw_data, customer, address_type): def rename_address(address, customer): old_address_title = address.name - new_address_title = customer.customer_name + "-" + address.address_type + new_address_title = customer.name + "-" + address.address_type address.address_title = customer.customer_name address.save() From 779fc898b187e1cd284d615a10556b014c4b0646 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sun, 17 May 2020 20:18:30 +0530 Subject: [PATCH 167/410] fix: Future date half day validation (#21718) * fix: Future date half day validation * fix: Allow half day attendance only via leave application Co-authored-by: Nabin Hait --- erpnext/hr/doctype/attendance/attendance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b6c80655c2e..6c7ee002db4 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -21,7 +21,7 @@ class Attendance(Document): date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") # leaves can be marked for future dates - if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()): + if self.status != 'On Leave' and not self.leave_application and getdate(self.attendance_date) > getdate(nowdate()): frappe.throw(_("Attendance can not be marked for future dates")) elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining): frappe.throw(_("Attendance date can not be less than employee's joining date")) @@ -41,7 +41,7 @@ class Attendance(Document): leave_record = frappe.db.sql(""" select leave_type, half_day, half_day_date from `tabLeave Application` - where employee = %s + where employee = %s and %s between from_date and to_date and status = 'Approved' and docstatus = 1 From 8e3fc5ee6eccb572a30794741de9dafe4a29b746 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 17 May 2020 20:28:39 +0530 Subject: [PATCH 168/410] fix: incorrect stock valuation for repack entry (#21736) --- .../stock/doctype/stock_entry/stock_entry.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 62c9eb1eb2c..be2dd526a65 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -363,6 +363,9 @@ class StockEntry(StockController): + self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError) def set_incoming_rate(self): + if self.purpose == "Repack": + self.set_basic_rate_for_finished_goods() + for d in self.items: if d.s_warehouse: args = self.get_args_for_incoming_rate(d) @@ -475,20 +478,31 @@ class StockEntry(StockController): "allow_zero_valuation": item.allow_zero_valuation_rate, }) - def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost): + def set_basic_rate_for_finished_goods(self, raw_material_cost=0, scrap_material_cost=0): + total_fg_qty = 0 + if not raw_material_cost and self.get("items"): + raw_material_cost = sum([flt(row.basic_amount) for row in self.items + if row.s_warehouse and not row.t_warehouse]) + + total_fg_qty = sum([flt(row.qty) for row in self.items + if row.t_warehouse and not row.s_warehouse]) + if self.purpose in ["Manufacture", "Repack"]: for d in self.get("items"): if (d.transfer_qty and (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)): - if self.work_order \ - and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + if (self.work_order and self.purpose == "Manufacture" + and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")): bom_items = self.get_bom_raw_materials(d.transfer_qty) raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) - if raw_material_cost: + if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) + elif self.purpose == "Repack" and total_fg_qty: + d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) + d.basic_amount = d.basic_rate * d.qty def distribute_additional_costs(self): if self.purpose == "Material Issue": From 3bce13eaa2d203fb5225f42acae504c782954202 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Sun, 17 May 2020 17:28:37 +0200 Subject: [PATCH 169/410] fix: remove guest access (#21693) --- erpnext/crm/doctype/lead/lead.json | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 20ab51d44bc..6fef0c46437 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_events_in_timeline": 1, "allow_import": 1, "autoname": "naming_series:", @@ -447,7 +448,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2020-04-08 22:26:11.687110", + "modified": "2020-05-11 20:27:45.868960", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -504,15 +505,6 @@ "read": 1, "report": 1, "role": "Sales User" - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Guest", - "share": 1 } ], "search_fields": "lead_name,lead_owner,status", From 40d49306d3b0e28e820995698bd85841049008cb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 17 May 2020 21:46:42 +0530 Subject: [PATCH 170/410] fix: pass ignore_mandatory flag when updating customer from patient --- erpnext/healthcare/doctype/patient/patient.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py index ea63ac74922..30a1e45f0ee 100644 --- a/erpnext/healthcare/doctype/patient/patient.py +++ b/erpnext/healthcare/doctype/patient/patient.py @@ -44,6 +44,7 @@ class Patient(Document): customer.default_price_list = self.default_price_list customer.default_currency = self.default_currency customer.language = self.language + customer.ignore_mandatory = True customer.save(ignore_permissions=True) else: if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'): From 30314f18f76ce051857c0c6a33380306c2984e25 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 17 May 2020 22:06:02 +0530 Subject: [PATCH 171/410] fix: patient appointment title --- .../doctype/patient_appointment/patient_appointment.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 94c6919b84f..9eb6e77c855 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -30,9 +30,8 @@ class PatientAppointment(Document): send_confirmation_msg(self) def set_title(self): - self.title = _('{0} with {1} on {2}').format(self.patient_name or self.patient, - self.practitioner_name or self.practitioner, - frappe.utils.format_datetime(self.appointment_datetime))[:100] + self.title = _('{0} with {1}').format(self.patient_name or self.patient, + self.practitioner_name or self.practitioner) def set_status(self): today = getdate() @@ -379,7 +378,7 @@ def send_appointment_reminder(): frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1) def send_message(doc, message): - patient_mobile = frappe.db.get_value("Patient", doc.patient, "mobile") + patient_mobile = frappe.db.get_value('Patient', doc.patient, 'mobile') if patient_mobile: context = {'doc': doc, 'alert': doc, 'comments': None} if doc.get('_comments'): @@ -391,7 +390,7 @@ def send_message(doc, message): try: send_sms(number, message) except Exception as e: - frappe.msgprint(_("SMS not sent, please check SMS Settings"), alert=True) + frappe.msgprint(_('SMS not sent, please check SMS Settings'), alert=True) @frappe.whitelist() def get_events(start, end, filters=None): From 2888e12dfc9fb2e67379395cb177db06d85810dc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 17 May 2020 22:23:53 +0530 Subject: [PATCH 172/410] fix: vital signs title field --- erpnext/healthcare/doctype/vital_signs/vital_signs.json | 3 ++- erpnext/healthcare/doctype/vital_signs/vital_signs.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json index 57fc2369e19..15ab5047bc4 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json @@ -247,6 +247,7 @@ }, { "allow_on_submit": 1, + "columns": 5, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -258,7 +259,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 23:18:08.278064", + "modified": "2020-05-17 22:23:24.632286", "modified_by": "Administrator", "module": "Healthcare", "name": "Vital Signs", diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index 43c6205b8e0..69d81ff4b08 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -19,8 +19,8 @@ class VitalSigns(Document): delete_vital_signs_from_medical_record(self) def set_title(self): - self.title = _('{0} on {1} {2}').format(self.patient_name or self.patient, - frappe.utils.format_date(self.signs_date), frappe.utils.format_time(self.signs_time))[:100] + self.title = _('{0} on {1}').format(self.patient_name or self.patient, + frappe.utils.format_date(self.signs_date))[:100] def insert_vital_signs_to_medical_record(doc): subject = set_subject_field(doc) From d3a5df2202c8ab50dcfba54eb4a51116ee9499e1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 17 May 2020 22:34:42 +0530 Subject: [PATCH 173/410] fix: codacy issues --- .../healthcare/doctype/patient_encounter/patient_encounter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 20ee9daedee..2410f8e10dd 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Patient Encounter', { refresh_field('lab_test_prescription'); if (!frm.doc.__islocal) { - if (frm.doc.docstatus == 1) { + if (frm.doc.docstatus === 1) { if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') { frm.add_custom_button(__('Schedule Discharge'), function() { schedule_discharge(frm); @@ -103,7 +103,7 @@ frappe.ui.form.on('Patient Encounter', { }, practitioner: function(frm) { - if(!frm.doc.practitioner) { + if (!frm.doc.practitioner) { frm.set_value('practitioner_name', ''); } }, From 53934b7e3b9725557188ec1eabc4f0536d8a1ae7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 17 May 2020 22:44:19 +0530 Subject: [PATCH 174/410] fix: failing test due to inpatient record --- .../doctype/inpatient_record/test_inpatient_record.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index e15324c55bf..4c2d3f692a2 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -8,7 +8,6 @@ import unittest from frappe.utils import now_datetime, today from frappe.utils.make_random import get_random from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge -from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient class TestInpatientRecord(unittest.TestCase): def test_admit_and_discharge(self): @@ -112,3 +111,13 @@ def get_service_unit_type(): service_unit_type.save(ignore_permissions = True) return service_unit_type.name return service_unit_type + +def create_patient(): + patient = frappe.db.exists('Patient', '_Test IPD Patient') + if not patient: + patient = frappe.new_doc('Patient') + patient.first_name = '_Test IPD Patient' + patient.sex = 'Female' + patient.save(ignore_permissions=True) + patient = patient.name + return patient From 1862f5f29a04b260940ddee7dc2f2c907a28ec26 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 18 May 2020 07:37:04 +0530 Subject: [PATCH 175/410] feat: Payroll based on employee cost center (#21609) --- erpnext/hr/doctype/department/department.json | 14 ++++- erpnext/hr/doctype/employee/employee.json | 16 ++++- erpnext/hr/doctype/employee/test_employee.py | 9 ++- .../hr/doctype/payroll_entry/payroll_entry.py | 39 +++++++----- .../payroll_entry/test_payroll_entry.py | 60 +++++++++++++++++-- .../hr/doctype/salary_slip/salary_slip.json | 12 +++- .../doctype/salary_slip/test_salary_slip.py | 26 +++++--- .../salary_structure/salary_structure.py | 6 +- .../salary_structure/test_salary_structure.py | 1 + 9 files changed, 147 insertions(+), 36 deletions(-) diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json index 6469f4cbed2..a54c1d18e7c 100644 --- a/erpnext/hr/doctype/department/department.json +++ b/erpnext/hr/doctype/department/department.json @@ -14,6 +14,8 @@ "is_group", "disabled", "section_break_4", + "payroll_cost_center", + "column_break_9", "leave_block_list", "leave_section", "leave_approvers", @@ -125,13 +127,23 @@ { "fieldname": "column_break_3", "fieldtype": "Column Break" + }, + { + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" } ], "icon": "fa fa-sitemap", "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:03:27.784362", + "modified": "2020-05-05 18:49:28.503931", "modified_by": "Administrator", "module": "HR", "name": "Department", diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 13c202c7759..f575765f69b 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -60,6 +60,8 @@ "default_shift", "salary_information", "salary_mode", + "payroll_cost_center", + "column_break_52", "bank_name", "bank_ac_no", "health_insurance_section", @@ -783,13 +785,25 @@ { "fieldname": "column_break_19", "fieldtype": "Column Break" + }, + { + "fetch_from": "department.payroll_cost_center", + "fetch_if_empty": 1, + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "column_break_52", + "fieldtype": "Column Break" } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", "links": [], - "modified": "2020-04-08 12:25:34.306695", + "modified": "2020-05-05 18:51:03.152503", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index d3410de2eb7..f4b214adc3c 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -45,7 +45,7 @@ class TestEmployee(unittest.TestCase): employee1_doc.status = 'Left' self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) -def make_employee(user, company=None): +def make_employee(user, company=None, **kwargs): if not frappe.db.get_value("User", user): frappe.get_doc({ "doctype": "User", @@ -55,7 +55,7 @@ def make_employee(user, company=None): "roles": [{"doctype": "Has Role", "role": "Employee"}] }).insert() - if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }): + if not frappe.db.get_value("Employee", {"user_id": user}): employee = frappe.get_doc({ "doctype": "Employee", "naming_series": "EMP-", @@ -71,7 +71,10 @@ def make_employee(user, company=None): "prefered_email": user, "status": "Active", "employment_type": "Intern" - }).insert() + }) + if kwargs: + employee.update(kwargs) + employee.insert() return employee.name else: return frappe.get_value("Employee", {"employee_name":user}, "name") diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py index 9ef3a99930b..656de0170dd 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py @@ -55,6 +55,7 @@ class PayrollEntry(Document): ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s {condition}""".format(condition=condition), {"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet}) + if sal_struct: cond += "and t2.salary_structure IN %(sal_struct)s " cond += "and %(from_date)s >= t2.from_date" @@ -138,7 +139,7 @@ class PayrollEntry(Document): cond = self.get_filter_condition() ss_list = frappe.db.sql(""" - select t1.name, t1.salary_structure from `tabSalary Slip` t1 + select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1 where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s """ % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict) @@ -169,10 +170,14 @@ class PayrollEntry(Document): def get_salary_components(self, component_type): salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True) - if salary_slips: - salary_components = frappe.db.sql("""select salary_component, amount, parentfield - from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" % - (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True) + if salary_slips: + salary_components = frappe.db.sql(""" + select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center + from `tabSalary Slip` ss, `tabSalary Detail` ssd + where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s) + """ % (component_type, ', '.join(['%s']*len(salary_slips))), + tuple([d.name for d in salary_slips]), as_dict=True) + return salary_components def get_salary_component_total(self, component_type = None): @@ -186,15 +191,16 @@ class PayrollEntry(Document): if is_flexible_benefit == 1 and only_tax_impact ==1: add_component_to_accrual_jv_entry = False if add_component_to_accrual_jv_entry: - component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount'] + component_dict[(item.salary_component, item.payroll_cost_center)] \ + = component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount) account_details = self.get_account(component_dict = component_dict) return account_details def get_account(self, component_dict = None): - account_dict = {} - for s, a in component_dict.items(): - account = self.get_salary_component_account(s) - account_dict[account] = account_dict.get(account, 0) + a + account_dict = {} + for key, amount in component_dict.items(): + account = self.get_salary_component_account(key[0]) + account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount return account_dict def get_default_payroll_payable_account(self): @@ -227,23 +233,23 @@ class PayrollEntry(Document): payable_amount = 0 # Earnings - for acc, amount in earnings.items(): + for acc_cc, amount in earnings.items(): payable_amount += flt(amount, precision) accounts.append({ - "account": acc, + "account": acc_cc[0], "debit_in_account_currency": flt(amount, precision), "party_type": '', - "cost_center": self.cost_center, + "cost_center": acc_cc[1] or self.cost_center, "project": self.project }) # Deductions - for acc, amount in deductions.items(): + for acc_cc, amount in deductions.items(): payable_amount -= flt(amount, precision) accounts.append({ - "account": acc, + "account": acc_cc[0], "credit_in_account_currency": flt(amount, precision), - "cost_center": self.cost_center, + "cost_center": acc_cc[1] or self.cost_center, "party_type": '', "project": self.project }) @@ -253,6 +259,7 @@ class PayrollEntry(Document): "account": default_payroll_payable_account, "credit_in_account_currency": flt(payable_amount, precision), "party_type": '', + "cost_center": self.cost_center }) journal_entry.set("accounts", accounts) diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py index e43f744bd43..3c318e78a24 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py @@ -10,15 +10,16 @@ from frappe.utils import add_months from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ - make_earning_salary_component, make_deduction_salary_component + make_earning_salary_component, make_deduction_salary_component, create_account from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans class TestPayrollEntry(unittest.TestCase): def setUp(self): - for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Salary Structure"]: - frappe.db.sql("delete from `tab%s`" % dt) + for dt in ["Salary Slip", "Salary Component", "Salary Component Account", + "Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]: + frappe.db.sql("delete from `tab%s`" % dt) make_earning_salary_component(setup=True, company_list=["_Test Company"]) make_deduction_salary_component(setup=True, company_list=["_Test Company"]) @@ -33,11 +34,59 @@ class TestPayrollEntry(unittest.TestCase): get_salary_component_account(data.name) employee = frappe.db.get_value("Employee", {'company': company}) - make_salary_structure("_Test Salary Structure", "Monthly", employee) + make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company) dates = get_start_end_dates('Monthly', nowdate()) if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}): make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date) + def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use + for data in frappe.get_all('Salary Component', fields = ["name"]): + if not frappe.db.get_value('Salary Component Account', + {'parent': data.name, 'company': "_Test Company"}, 'name'): + get_salary_component_account(data.name) + + if not frappe.db.exists('Department', "cc - _TC"): + frappe.get_doc({ + 'doctype': 'Department', + 'department_name': "cc", + "company": "_Test Company" + }).insert() + + employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC", + department="cc - _TC", company="_Test Company") + employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC", + department="cc - _TC", company="_Test Company") + + make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company") + make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company") + + if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"): + create_account(account_name="_Test Payroll Payable", + company="_Test Company", parent_account="Current Liabilities - _TC") + frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account", + "_Test Payroll Payable - _TC") + + dates = get_start_end_dates('Monthly', nowdate()) + if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}): + pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, + department="cc - _TC", company="_Test Company", payment_account="Cash - _TC", cost_center="Main - _TC") + je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry") + je_entries = frappe.db.sql(""" + select account, cost_center, debit, credit + from `tabJournal Entry Account` + where parent=%s + order by account, cost_center + """, je) + expected_je = ( + ('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0), + ('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0), + ('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0), + ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0), + ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0) + ) + + self.assertEqual(je_entries, expected_je) + def test_get_end_date(self): self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'}) self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'}) @@ -49,7 +98,6 @@ class TestPayrollEntry(unittest.TestCase): self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'}) def test_loan(self): - branch = "Test Employee Branch" applicant = make_employee("test_employee@loan.com", company="_Test Company") company = "_Test Company" @@ -116,6 +164,7 @@ def make_payroll_entry(**args): payroll_entry.posting_date = nowdate() payroll_entry.payroll_frequency = "Monthly" payroll_entry.branch = args.branch or None + payroll_entry.department = args.department or None if args.cost_center: payroll_entry.cost_center = args.cost_center @@ -123,6 +172,7 @@ def make_payroll_entry(**args): if args.payment_account: payroll_entry.payment_account = args.payment_account + payroll_entry.fill_employee_details() payroll_entry.save() payroll_entry.create_salary_slips() payroll_entry.submit_salary_slips() diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index 54a8164587e..cfd4d897314 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -12,6 +12,7 @@ "department", "designation", "branch", + "payroll_cost_center", "column_break1", "status", "journal_entry", @@ -459,13 +460,22 @@ "options": "Salary Slip", "print_hide": 1, "read_only": 1 + }, + { + "fetch_from": "employee.payroll_cost_center", + "fetch_if_empty": 1, + "fieldname": "payroll_cost_center", + "fieldtype": "Link", + "label": "Payroll Cost Center", + "options": "Cost Center", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-04-14 20:02:53.159827", + "modified": "2020-05-05 18:55:26.173629", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index a7dcb941677..3eff738ec8d 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -422,22 +422,32 @@ def get_salary_component_account(sal_comp, company_list=None): sal_comp = frappe.get_doc("Salary Component", sal_comp) if not sal_comp.get("accounts"): for d in company_list: + company_abbr = frappe.get_cached_value('Company', d, 'abbr') + + if sal_comp.type == "Earning": + account_name = "Salary" + parent_account = "Indirect Expenses - " + company_abbr + else: + account_name = "Salary Deductions" + parent_account = "Current Liabilities - " + company_abbr + sal_comp.append("accounts", { "company": d, - "default_account": create_account(d) + "default_account": create_account(account_name, d, parent_account) }) sal_comp.save() -def create_account(company): - salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr')) - if not salary_account: +def create_account(account_name, company, parent_account): + company_abbr = frappe.get_cached_value('Company', company, 'abbr') + account = frappe.db.get_value("Account", account_name + " - " + company_abbr) + if not account: frappe.get_doc({ "doctype": "Account", - "account_name": "Salary", - "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'), + "account_name": account_name, + "parent_account": parent_account, "company": company }).insert() - return salary_account + return account def make_earning_salary_component(setup=False, test_tax=False, company_list=None): data = [ @@ -683,7 +693,7 @@ def setup_test(): make_earning_salary_component(setup=True, company_list=["_Test Company"]) make_deduction_salary_component(setup=True, company_list=["_Test Company"]) - for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]: + for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance", "Additional Salary"]: frappe.db.sql("delete from `tab%s`" % dt) make_holiday_list() diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 5ba7f1c4327..ffc16d73c25 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -153,12 +153,16 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = def postprocess(source, target): if employee: employee_details = frappe.db.get_value("Employee", employee, - ["employee_name", "branch", "designation", "department"], as_dict=1) + ["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1) target.employee = employee target.employee_name = employee_details.employee_name target.branch = employee_details.branch target.designation = employee_details.designation target.department = employee_details.department + target.payroll_cost_center = employee_details.payroll_cost_center + if not target.payroll_cost_center and target.department: + target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center") + target.run_method('process_salary_structure', for_preview=for_preview) doc = get_mapped_doc("Salary Structure", source_name, { diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index c1869f05d7d..eb5311e3b81 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -128,6 +128,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do salary_structure_doc.insert() if not dont_submit: salary_structure_doc.submit() + else: salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure) From f4f307c36a925ae4eb25cc0be4d9f95d353ea3b9 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 18 May 2020 11:18:44 +0530 Subject: [PATCH 176/410] fix: Patch to set status in old serial no data (#21720) * fix: Patch to set status in old serial no data * fix: Avoid get_doc in patch * fix: fetch all values and check status in one query --- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_serial_no_status.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 erpnext/patches/v12_0/set_serial_no_status.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ebbcccc7109..4ae591b54b5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -684,3 +684,4 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation +erpnext.patches.v12_0.set_serial_no_status diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py new file mode 100644 index 00000000000..4ec84ef0f9e --- /dev/null +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, nowdate + +def execute(): + frappe.reload_doc('stock', 'doctype', 'serial_no') + + for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1): + if serial_no.get("delivery_document_type"): + status = "Delivered" + elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): + status = "Expired" + else: + status = "Active" + + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file From ec8c28e48796169258b2788f78fa4c6de37eae5f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 18 May 2020 11:27:38 +0530 Subject: [PATCH 177/410] Revert "fix(accounting): Allow 0 threshold in Tax Withholding Category" --- .../tax_withholding_category/tax_withholding_category.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 87fe3715bf0..4d43919f62e 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -162,7 +162,8 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date) supplier_credit_amount -= debit_note_amount - if supplier_credit_amount >= tax_details.get('threshold', 0) or supplier_credit_amount >= tax_details.get('cumulative_threshold', 0): + if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold) + or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)): if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total, ldc.certificate_limit): @@ -224,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount, certificate_limit > deducted_amount): valid = True - return valid + return valid \ No newline at end of file From 34f4a2398b7734fac40f8290ade81d6c914c38b1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 18 Apr 2020 18:59:32 +0530 Subject: [PATCH 178/410] feat: manufacturing dashboards --- erpnext/config/manufacturing.py | 14 +- .../manufacturing/manufacturing.json | 31 ++- .../doctype/downtime_entry/__init__.py | 0 .../doctype/downtime_entry/downtime_entry.js | 8 + .../downtime_entry/downtime_entry.json | 120 +++++++++ .../doctype/downtime_entry/downtime_entry.py | 13 + .../downtime_entry/test_downtime_entry.py | 10 + .../doctype/job_card/job_card.json | 13 +- .../doctype/work_order/work_order.json | 25 +- .../doctype/work_order/work_order.py | 27 +- .../report/downtime_analysis/__init__.py | 0 .../downtime_analysis/downtime_analysis.js | 28 ++ .../downtime_analysis/downtime_analysis.json | 31 +++ .../downtime_analysis/downtime_analysis.py | 96 +++++++ .../report/job_card_summary/__init__.py | 0 .../job_card_summary/job_card_summary.js | 52 ++++ .../job_card_summary/job_card_summary.json | 34 +++ .../job_card_summary/job_card_summary.py | 197 ++++++++++++++ .../production_analytics.py | 13 +- .../quality_inspection_summary/__init__.py | 0 .../quality_inspection_summary.js | 40 +++ .../quality_inspection_summary.json | 32 +++ .../quality_inspection_summary.py | 133 ++++++++++ .../report/work_order_summary/__init__.py | 0 .../work_order_summary/work_order_summary.js | 58 ++++ .../work_order_summary.json | 31 +++ .../work_order_summary/work_order_summary.py | 173 ++++++++++++ erpnext/patches.txt | 1 + erpnext/patches/v13_0/__init__.py | 1 + .../update_actual_start_and_end_date_in_wo.py | 43 +++ .../setup_wizard/data/dashboard_charts.py | 247 ++++++++++++++++++ .../quality_inspection.json | 16 +- .../stock/doctype/stock_entry/stock_entry.py | 4 + 33 files changed, 1444 insertions(+), 47 deletions(-) create mode 100644 erpnext/manufacturing/doctype/downtime_entry/__init__.py create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py create mode 100644 erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py create mode 100644 erpnext/manufacturing/report/downtime_analysis/__init__.py create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py create mode 100644 erpnext/manufacturing/report/job_card_summary/__init__.py create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.js create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.json create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.py create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/__init__.py create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py create mode 100644 erpnext/manufacturing/report/work_order_summary/__init__.py create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.js create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.json create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.py create mode 100644 erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py create mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index 2c18eeb83a1..012f1cad0ad 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -120,13 +120,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Open Work Orders", - "doctype": "Work Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Work Orders in Progress", + "name": "Work Order Summary", "doctype": "Work Order" }, { @@ -135,12 +129,6 @@ def get_data(): "name": "Issued Items Against Work Order", "doctype": "Work Order" }, - { - "type": "report", - "is_query_report": True, - "name": "Completed Work Orders", - "doctype": "Work Order" - }, { "type": "report", "is_query_report": True, diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 18604e283ab..0464b763c0e 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Open Work Orders\",\n \"name\": \"Open Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Orders in Progress\",\n \"name\": \"Work Orders in Progress\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Completed Work Orders\",\n \"name\": \"Completed Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Order Summary\",\n \"name\": \"Work Order Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quality Inspection\"\n ],\n \"doctype\": \"Quality Inspection\",\n \"is_query_report\": true,\n \"label\": \"Quality Inspection Summary\",\n \"name\": \"Quality Inspection Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Downtime Entry\"\n ],\n \"doctype\": \"Downtime Entry\",\n \"is_query_report\": true,\n \"label\": \"Downtime Analysis\",\n \"name\": \"Downtime Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Job Card\"\n ],\n \"doctype\": \"Job Card\",\n \"is_query_report\": true,\n \"label\": \"Job Card Summary\",\n \"name\": \"Job Card Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -32,7 +32,12 @@ } ], "category": "Domains", - "charts": [], + "charts": [ + { + "chart_name": "Production Analysis", + "label": "Production Analysis" + } + ], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -42,7 +47,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-04-01 11:28:50.979358", + "modified": "2020-04-27 00:17:26.323677", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -50,5 +55,21 @@ "pin_to_bottom": 0, "pin_to_top": 0, "restrict_to_domain": "Manufacturing", - "shortcuts": [] + "shortcuts": [ + { + "label": "Item", + "link_to": "Item", + "type": "DocType" + }, + { + "label": "BOM", + "link_to": "BOM", + "type": "DocType" + }, + { + "label": "Work Order", + "link_to": "Work Order", + "type": "DocType" + } + ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/__init__.py b/erpnext/manufacturing/doctype/downtime_entry/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js new file mode 100644 index 00000000000..3b7f5ba8d7f --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Downtime Entry', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json new file mode 100644 index 00000000000..6ec088ad9e7 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -0,0 +1,120 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2020-04-18 04:50:46.187638", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "workstation", + "operator", + "column_break_4", + "from_time", + "to_time", + "downtime", + "downtime_reason_section", + "reason" + ], + "fields": [ + { + "fieldname": "workstation", + "fieldtype": "Link", + "label": "Workstation / Machine", + "options": "Workstation", + "reqd": 1 + }, + { + "fieldname": "from_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "From Time", + "reqd": 1 + }, + { + "fieldname": "to_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "To Time", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "reason", + "fieldtype": "Text", + "label": "Reason", + "reqd": 1 + }, + { + "fieldname": "operator", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operator", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "downtime_reason_section", + "fieldtype": "Section Break", + "label": "Downtime Reason" + }, + { + "description": "In Mins", + "fieldname": "downtime", + "fieldtype": "Float", + "label": "Downtime", + "read_only": 1 + } + ], + "links": [], + "modified": "2020-04-20 17:34:51.299607", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Downtime Entry", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "workstation", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py new file mode 100644 index 00000000000..56ec4356af4 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import time_diff_in_hours +from frappe.model.document import Document + +class DowntimeEntry(Document): + def validate(self): + if self.from_time and self.to_time: + self.downtime = time_diff_in_hours(self.to_time, self.from_time) * 60 diff --git a/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py new file mode 100644 index 00000000000..8b2a8d36c12 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestDowntimeEntry(unittest.TestCase): + pass diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 7661fffa864..fba670c1c15 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2018-07-09 17:23:29.518745", "doctype": "DocType", @@ -264,8 +265,10 @@ { "fetch_from": "work_order.production_item", "fieldname": "production_item", - "fieldtype": "Read Only", - "label": "Production Item" + "fieldtype": "Link", + "label": "Production Item", + "options": "Item", + "read_only": 1 }, { "fieldname": "barcode", @@ -274,7 +277,8 @@ "read_only": 1 }, { - "fetch_from": "work_order.item_name", + "fetch_from": "production_item.item_name", + "fetch_if_empty": 1, "fieldname": "item_name", "fieldtype": "Read Only", "label": "Item Name" @@ -290,7 +294,8 @@ } ], "is_submittable": 1, - "modified": "2020-03-27 13:36:35.417502", + "links": [], + "modified": "2020-04-20 15:14:00.273441", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 00a67a03d67..8be22875a31 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -38,11 +38,12 @@ "required_items", "time", "planned_start_date", - "actual_start_date", - "column_break_13", "planned_end_date", - "actual_end_date", "expected_delivery_date", + "column_break_13", + "actual_start_date", + "actual_end_date", + "lead_time", "operations_section", "transfer_material_against", "operations", @@ -108,6 +109,8 @@ }, { "depends_on": "eval:doc.production_item", + "fetch_from": "production_item.item_name", + "fetch_if_empty": 1, "fieldname": "item_name", "fieldtype": "Data", "label": "Item Name", @@ -281,27 +284,30 @@ "reqd": 1 }, { + "allow_on_submit": 1, "fieldname": "actual_start_date", "fieldtype": "Datetime", "label": "Actual Start Date", - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { "fieldname": "column_break_13", "fieldtype": "Column Break" }, { + "allow_on_submit": 1, "fieldname": "planned_end_date", "fieldtype": "Datetime", "label": "Planned End Date", "no_copy": 1, - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { + "allow_on_submit": 1, "fieldname": "actual_end_date", "fieldtype": "Datetime", "label": "Actual End Date", - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { "allow_on_submit": 1, @@ -476,6 +482,13 @@ "fieldtype": "Link", "label": "Source Warehouse", "options": "Warehouse" + }, + { + "description": "In Mins", + "fieldname": "lead_time", + "fieldtype": "Float", + "label": "Lead Time", + "read_only": 1 } ], "icon": "fa fa-cogs", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 8301f30d837..c2789559b07 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -6,7 +6,7 @@ import frappe import json import math from frappe import _ -from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form +from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict from dateutil.relativedelta import relativedelta @@ -279,7 +279,7 @@ class WorkOrder(Document): if enable_capacity_planning and job_card_doc: row.planned_start_time = job_card_doc.time_logs[-1].from_time row.planned_end_time = job_card_doc.time_logs[-1].to_time - print(row.planned_start_time, original_start_time, plan_days) + if date_diff(row.planned_start_time, original_start_time) > plan_days: frappe.message_log.pop() frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.") @@ -437,8 +437,6 @@ class WorkOrder(Document): frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'")) def set_actual_dates(self): - self.actual_start_date = None - self.actual_end_date = None if self.get("operations"): actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time] if actual_start_dates: @@ -447,6 +445,27 @@ class WorkOrder(Document): actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time] if actual_end_dates: self.actual_end_date = max(actual_end_dates) + else: + data = frappe.get_all("Stock Entry", + fields = ["timestamp(posting_date, posting_time) as posting_datetime"], + filters = { + "work_order": self.name, + "purpose": ("in", ["Material Transfer for Manufacture", "Manufacture"]) + } + ) + + if data and len(data): + dates = [d.posting_datetime for d in data] + self.actual_start_date = min(dates) + + if self.status == "Completed": + self.actual_end_date = max(dates) + + self.set_lead_time() + + def set_lead_time(self): + if self.actual_start_date and self.actual_end_date: + self.lead_time = flt(time_diff_in_hours(self.actual_end_date, self.actual_start_date) * 60) def delete_job_card(self): for d in frappe.get_all("Job Card", ["name"], {"work_order": self.name}): diff --git a/erpnext/manufacturing/report/downtime_analysis/__init__.py b/erpnext/manufacturing/report/downtime_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js new file mode 100644 index 00000000000..e20342792f4 --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -0,0 +1,28 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Downtime Analysis"] = { + "filters": [ + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Machine"), + fieldname: "workstation", + fieldtype: "Link", + options: "Workstation" + } + ] +}; diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json new file mode 100644 index 00000000000..5edc7781a2e --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 1, + "creation": "2020-04-20 18:26:04.345289", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-20 18:26:04.345289", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Downtime Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Downtime Entry", + "report_name": "Downtime Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py new file mode 100644 index 00000000000..d69ec189baa --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -0,0 +1,96 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import flt +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {} + + fields = ["workstation", "operator", "from_time", "to_time", "downtime", "reason"] + + query_filters["from_time"] = (">=", filters.get("from_date")) + query_filters["to_time"] = ("<=", filters.get("to_date")) + + if filters.get("workstation"): + query_filters["workstation"] = filters.get("workstation") + + return frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) + +def get_chart_data(data, columns): + labels = sorted(list(set([d.workstation for d in data]))) + + workstation_wise_data = {} + for d in data: + if d.workstation not in workstation_wise_data: + workstation_wise_data[d.workstation] = 0 + + workstation_wise_data[d.workstation] += flt(d.downtime, 2) + + datasets = [] + for label in labels: + datasets.append(workstation_wise_data.get(label, 0)) + + chart = { + "data": { + "labels": labels, + "datasets": [ + {"name": "Dataset 1", "values": datasets} + ] + }, + "type": "bar", + "colors": ["#ff5858"] + } + + return chart + +def get_columns(filters): + return [ + { + "label": _("Machine"), + "fieldname": "workstation", + "fieldtype": "Link", + "options": "Workstation", + "width": 100 + }, + { + "label": _("Operator"), + "fieldname": "operator", + "fieldtype": "Link", + "options": "Employee", + "width": 130 + }, + { + "label": _("From Time"), + "fieldname": "from_time", + "fieldtype": "Datetime", + "width": 160 + }, + { + "label": _("To Time"), + "fieldname": "to_time", + "fieldtype": "Datetime", + "width": 160 + }, + { + "label": _("Downtime (In Mins)"), + "fieldname": "downtime", + "fieldtype": "Float", + "width": 150 + }, + { + "label": _("Reason"), + "fieldname": "reason", + "fieldtype": "Text", + "width": 180 + } + ] \ No newline at end of file diff --git a/erpnext/manufacturing/report/job_card_summary/__init__.py b/erpnext/manufacturing/report/job_card_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js new file mode 100644 index 00000000000..b7e307183f8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -0,0 +1,52 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Job Card Summary"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Open", "Work In Progress", "Completed", "On Hold"] + }, + { + label: __("Sales Orders"), + fieldname: "sales_order", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Sales Order', txt); + } + }, + { + label: __("Production Item"), + fieldname: "production_item", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Item', txt); + } + } + ] +}; diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json new file mode 100644 index 00000000000..9f08fc34cb8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json @@ -0,0 +1,34 @@ +{ + "add_total_row": 0, + "creation": "2020-04-20 12:00:21.436619", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-20 12:00:21.436619", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Job Card", + "report_name": "Job Card Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py new file mode 100644 index 00000000000..0f60c132ec8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -0,0 +1,197 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import getdate, flt +from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period) + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {"docstatus": ("<", 2)} + + fields = ["name", "status", "work_order", "production_item", "item_name", + "total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"] + + for field in ["work_order", "workstation", "operation", "company"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + data = frappe.get_all("Job Card", + fields= fields, filters=query_filters) + + if not data: return [] + + job_cards = [d.name for d in data] + job_card_time_details = {} + for job_card_data in frappe.get_all("Job Card Time Log", + fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"], + filters={"docstatus": ("<", 2), "parent": ("in", job_cards)}, group_by="parent"): + job_card_time_details[job_card_data.parent] = job_card_data + + for d in data: + if d.status == "Material Transferred": + d.status = "Open" + + if job_card_time_details.get(d.name): + d.from_time = job_card_time_details.get(d.name).from_time + d.to_time = job_card_time_details.get(d.name).to_time + + return data + +def get_chart_data(job_card_details, filters): + labels, periodic_data = prepare_chart_data(job_card_details, filters) + + not_start, in_progress, on_hold, completed = [], [], [] , [] + datasets = [] + + for d in labels: + not_start.append(periodic_data.get("Open").get(d)) + in_progress.append(periodic_data.get("Work In Progress").get(d)) + on_hold.append(periodic_data.get("On Hold").get(d)) + completed.append(periodic_data.get("Completed").get(d)) + + datasets.append({'name':'Open', 'values': not_start}) + datasets.append({'name':'Work In Progress', 'values': in_progress}) + datasets.append({'name':'On Hold', 'values': on_hold}) + datasets.append({'name':'Completed', 'values': completed}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar", + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"], + "axisOptions": { + "xAxisMode": "tick" + }, + "barOptions": { + "stacked": 1 + } + } + + return chart + +def prepare_chart_data(job_card_details, filters): + labels = [] + + periodic_data = { + "Open": {}, + "Work In Progress": {}, + "On Hold": {}, + "Completed": {} + } + + filters.range = "Monthly" + + ranges = get_period_date_ranges(filters) + for from_date, end_date in ranges: + period = get_period(end_date, filters) + if period not in labels: + labels.append(period) + + for d in job_card_details: + if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: + if periodic_data.get(d.status) and periodic_data.get(d.status).get(period): + periodic_data[d.status][period] += 1 + else: + periodic_data[d.status][period] = 1 + + return labels, periodic_data + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Job Card", + "width": 100 + }, + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Work Order"), + "fieldname": "work_order", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + { + "label": _("Production Item"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 110 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("Workstation"), + "fieldname": "workstation", + "fieldtype": "Link", + "options": "Workstation", + "width": 110 + }, + { + "label": _("Operation"), + "fieldname": "operation", + "fieldtype": "Link", + "options": "Operation", + "width": 110 + }, + { + "label": _("Employee Name"), + "fieldname": "employee_name", + "fieldtype": "Data", + "width": 110 + }, + { + "label": _("Total Completed Qty"), + "fieldname": "total_completed_qty", + "fieldtype": "Float", + "width": 120 + }, + { + "label": _("From Time"), + "fieldname": "from_time", + "fieldtype": "Datetime", + "width": 120 + }, + { + "label": _("To Time"), + "fieldname": "to_time", + "fieldtype": "Datetime", + "width": 120 + }, + { + "label": _("Time Required (In Mins)"), + "fieldname": "total_time_in_mins", + "fieldtype": "Float", + "width": 100 + } + ]) + + return columns \ No newline at end of file diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 7447a1f6705..f0bdfeda21c 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -38,7 +38,6 @@ def get_columns(filters): def get_periodic_data(filters, entry): periodic_data = { - "All Work Orders": {}, "Not Started": {}, "Overdue": {}, "Pending": {}, @@ -51,7 +50,6 @@ def get_periodic_data(filters, entry): period = get_period(end_date, filters) for d in entry: if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) : - periodic_data = update_periodic_data(periodic_data, "All Work Orders", period) if d.status == 'Completed': if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date): periodic_data = update_periodic_data(periodic_data, "Completed", period) @@ -61,7 +59,7 @@ def get_periodic_data(filters, entry): elif getdate(d.planned_start_date) < getdate(from_date) : periodic_data = update_periodic_data(periodic_data, "Overdue", period) - + else: periodic_data = update_periodic_data(periodic_data, "Not Started", period) @@ -99,7 +97,7 @@ def get_data(filters, columns): periodic_data = get_periodic_data(filters,entry) - labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"] + labels = ["Not Started", "Overdue", "Pending", "Completed"] chart_data = get_chart_data(periodic_data,columns) ranges = get_period_date_ranges(filters) @@ -123,13 +121,11 @@ def get_chart_data(periodic_data, columns): datasets = [] for d in labels: - all_data.append(periodic_data.get("All Work Orders").get(d)) not_start.append(periodic_data.get("Not Started").get(d)) overdue.append(periodic_data.get("Overdue").get(d)) pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({'name':'All Work Orders', 'values': all_data}) datasets.append({'name':'Not Started', 'values': not_start}) datasets.append({'name':'Overdue', 'values': overdue}) datasets.append({'name':'Pending', 'values': pending}) @@ -139,9 +135,10 @@ def get_chart_data(periodic_data, columns): "data": { 'labels': labels, 'datasets': datasets - } + }, + "type": "bar", + "colors": ["#5e64ff", "#ff5858", "#ffa00a", "#98d85b"] } - chart["type"] = "line" return chart diff --git a/erpnext/manufacturing/report/quality_inspection_summary/__init__.py b/erpnext/manufacturing/report/quality_inspection_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js new file mode 100644 index 00000000000..d4587aa6619 --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js @@ -0,0 +1,40 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Quality Inspection Summary"] = { + "filters": [ + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Accepted", "Rejected"] + }, + { + label: __("Item Code"), + fieldname: "item_code", + fieldtype: "Link", + options: "Item" + }, + { + label: __("Inspected By"), + fieldname: "inspected_by", + fieldtype: "Link", + options: "User" + } + ] +}; diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json new file mode 100644 index 00000000000..48226e6b21d --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "creation": "2020-04-26 18:23:53.475110", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "json": "{}", + "letter_head": "Gadgets International", + "modified": "2020-04-26 18:24:50.529940", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Quality Inspection Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Quality Inspection", + "report_name": "Quality Inspection Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Quality Manager" + }, + { + "role": "Stock User" + }, + { + "role": "Stock Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py new file mode 100644 index 00000000000..9c1bfa46301 --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -0,0 +1,133 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data , None, chart_data + +def get_data(filters): + query_filters = {"docstatus": ("<", 2)} + + fields = ["name", "status", "report_date", "item_code", "item_name", "sample_size", + "inspection_type", "reference_type", "reference_name", "inspected_by"] + + for field in ["status", "item_code", "status", "inspected_by"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + query_filters["report_date"] = (">=", filters.get("from_date")) + query_filters["report_date"] = ("<=", filters.get("to_date")) + + return frappe.get_all("Quality Inspection", + fields= fields, filters=query_filters, order_by="report_date asc") + +def get_chart_data(periodic_data, columns): + labels = ["Rejected", "Accepted"] + + status_wise_data = { + "Accepted": 0, + "Rejected": 0 + } + + datasets = [] + + for d in periodic_data: + status_wise_data[d.status] += 1 + + datasets.append({'name':'Qty Wise Chart', + 'values': [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")]}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "donut", + "height": 300, + "colors": ["#ff5858", "#98d85b"] + } + + return chart + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + { + "label": _("Report Date"), + "fieldname": "report_date", + "fieldtype": "Date", + "width": 150 + } + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 130 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Sample Size"), + "fieldname": "sample_size", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Inspection Type"), + "fieldname": "inspection_type", + "fieldtype": "Data", + "width": 110 + }, + { + "label": _("Document Type"), + "fieldname": "reference_type", + "fieldtype": "Data", + "width": 90 + }, + { + "label": _("Document Name"), + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "options": "reference_type", + "width": 150 + }, + { + "label": _("Inspected By"), + "fieldname": "inspected_by", + "fieldtype": "Link", + "options": "User", + "width": 150 + } + ]) + + return columns \ No newline at end of file diff --git a/erpnext/manufacturing/report/work_order_summary/__init__.py b/erpnext/manufacturing/report/work_order_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js new file mode 100644 index 00000000000..33b147df49a --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -0,0 +1,58 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Work Order Summary"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Not Started", "In Process", "Completed", "Stopped"] + }, + { + label: __("Sales Orders"), + fieldname: "sales_order", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Sales Order', txt); + } + }, + { + label: __("Production Item"), + fieldname: "production_item", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Item', txt); + } + }, + { + label: __("Age"), + fieldname:"age", + fieldtype: "Int", + default: "0" + }, + ] +}; diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.json b/erpnext/manufacturing/report/work_order_summary/work_order_summary.json new file mode 100644 index 00000000000..0d093e22e94 --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2020-04-17 17:07:56.830358", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-19 16:59:47.979278", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Work Order Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Work Order", + "report_name": "Work Order Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py new file mode 100644 index 00000000000..92c31ab382e --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -0,0 +1,173 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import date_diff, today +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + + if not filters.get("age"): + filters["age"] = 0 + + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {"docstatus": 1} + + fields = ["name", "status", "sales_order", "production_item", "qty", "produced_qty", + "planned_start_date", "planned_end_date", "actual_start_date", "actual_end_date", "lead_time"] + + for field in ["sales_order", "production_item", "status", "company"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + query_filters["planned_start_date"] = (">=", filters.get("from_date")) + query_filters["planned_end_date"] = ("<=", filters.get("to_date")) + + data = frappe.get_all("Work Order", + fields= fields, filters=query_filters, order_by="planned_start_date asc") + + res = [] + for d in data: + start_date = d.actual_start_date or d.planned_start_date + d.age = 0 + + if d.status != 'Completed': + d.age = date_diff(today(), start_date) + + if filters.get("age") <= d.age: + res.append(d) + + return res + +def get_chart_data(periodic_data, columns): + labels = ["Not Started", "In Process", "Stopped", "Completed"] + + status_wise_data = { + "Not Started": 0, + "In Process": 0, + "Stopped": 0, + "Completed": 0 + } + + for d in periodic_data: + if d.status == "In Process" and d.produced_qty: + status_wise_data["Completed"] += d.produced_qty + + status_wise_data[d.status] += d.qty + + values = [status_wise_data["Not Started"], status_wise_data["In Process"], + status_wise_data["Stopped"], status_wise_data["Completed"]] + + chart = { + "data": { + 'labels': labels, + 'datasets': [{'name':'Qty Wise Chart', 'values': values}] + }, + "type": "donut", + "height": 300, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + } + + return chart + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Production Item"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 130 + }, + { + "label": _("Produce Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Produced Qty"), + "fieldname": "produced_qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Sales Order"), + "fieldname": "sales_order", + "fieldtype": "Link", + "options": "Sales Order", + "width": 90 + }, + { + "label": _("Planned Start Date"), + "fieldname": "planned_start_date", + "fieldtype": "Date", + "width": 150 + }, + { + "label": _("Planned End Date"), + "fieldname": "planned_end_date", + "fieldtype": "Date", + "width": 150 + } + ]) + + if filters.get("status") != 'Not Started': + columns.extend([ + { + "label": _("Actual Start Date"), + "fieldname": "actual_start_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Actual End Date"), + "fieldname": "actual_end_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Age"), + "fieldname": "age", + "fieldtype": "Float", + "width": 110 + }, + ]) + + if filters.get("status") == 'Completed': + columns.extend([ + { + "label": _("Lead Time (in mins)"), + "fieldname": "lead_time", + "fieldtype": "Float", + "width": 110 + }, + ]) + + return columns \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..b920d0334f5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py index e69de29bb2d..baffc488252 100644 --- a/erpnext/patches/v13_0/__init__.py +++ b/erpnext/patches/v13_0/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py new file mode 100644 index 00000000000..3fab0040fb1 --- /dev/null +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -0,0 +1,43 @@ + +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.utils import add_to_date + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "work_order") + frappe.reload_doc("manufacturing", "doctype", "work_order_item") + frappe.reload_doc("manufacturing", "doctype", "job_card") + + data = frappe.get_all("Work Order", + filters = { + "docstatus": 1, + "status": ("in", ["In Process", "Completed"]) + }) + + for d in data: + doc = frappe.get_doc("Work Order", d.name) + doc.set_actual_dates() + doc.db_set("actual_start_date", doc.actual_start_date, update_modified=False) + + if doc.status == "Completed": + frappe.db.set_value("Work Order", d.name, { + "actual_end_date": doc.actual_end_date, + "lead_time": doc.lead_time + }, update_modified=False) + + if not doc.planned_end_date: + planned_end_date = add_to_date(doc.planned_start_date, minutes=doc.lead_time) + doc.db_set("planned_end_date", doc.actual_start_date, update_modified=False) + + + frappe.db.sql(""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo + SET + jc.production_item = wo.production_item, jc.item_name = wo.item_name + WHERE + jc.work_order = wo.name and IFNULL(jc.production_item, "") = "" + """) + diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py new file mode 100644 index 00000000000..f5b66817672 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -0,0 +1,247 @@ +from __future__ import unicode_literals +from frappe import _ +import frappe +import json + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_default_dashboards(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + income_account = company.default_income_account or get_account("Income Account", company.name) + expense_account = company.default_expense_account or get_account("Expense Account", company.name) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return { + "Dashboards": [ + { + "doctype": "Dashboard", + "dashboard_name": "Accounts", + "charts": [ + { "chart": "Outgoing Bills (Sales Invoice)" }, + { "chart": "Incoming Bills (Purchase Invoice)" }, + { "chart": "Bank Balance" }, + { "chart": "Income" }, + { "chart": "Expenses" }, + { "chart": "Patient Appointments" } + ] + }, + { + "doctype": "Dashboard", + "dashboard_name": "Manufacturing", + "charts": [ + { "chart": "Work Order Analysis", "width": "Half" }, + { "chart": "Quality Inspection Analysis", "width": "Half" }, + { "chart": "Long Time Pending Work Orders", "width": "Half" }, + { "chart": "Downtime Analysis", "width": "Half" }, + { "chart": "Production Analysis", "width": "Full" }, + { "chart": "Job Card Analysis", "width": "Full" } + ] + } + ], + "Charts": [ + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Daily", + "chart_name": "Patient Appointments", + "timespan": "Last Month", + "color": "#77ecca", + "filters_json": json.dumps({}), + "chart_type": "Count", + "timeseries": 1, + "based_on": "appointment_datetime", + "owner": "Administrator", + "document_type": "Patient Appointment", + "type": "Line", + "width": "Half" + } + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Work Order Analysis", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name}), + "bar": "Donut", + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Quality Inspection Analysis", + "timespan": "Last Year", + "report_name": "Quality Inspection Summary", + "owner": "Administrator", + "filters_json": json.dumps({}), + "bar": "Donut", + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300, + "colors": ["#ff5858", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Long Time Pending Work Orders", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company.name, "age":180}), + "owner": "Administrator", + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#ff5858"], + "x_field": "name", + "y_fields": ["age"], + "y_axis_fields": [{"__islocal": "true", "idx": 1, "y_field": "age"}], + "chart_type": "Bar" + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Downtime Analysis", + "timespan": "Last Year", + "filters_json": json.dumps({}), + "report_name": "Downtime Analysis", + "owner": "Administrator", + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#ff5858"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Production Analysis", + "timespan": "Last Year", + "report_name": "Production Analytics", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#7cd6fd", "#ff5858", "#ffa00a", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Job Card Analysis", + "timespan": "Last Year", + "report_name": "Job Card Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), + "bar": "Bar", + "custom_options": json.dumps({ + "axisOptions": { + "xAxisMode": "tick" + }, + "barOptions": { + "stacked": 1 + }, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + }), + }, + ] + } + +def get_account(account_type, company): + accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) + if accounts: + return accounts[0].name diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index a9f3cd09ef5..c951066aa83 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2013-04-30 13:13:03", "doctype": "DocType", @@ -8,6 +9,7 @@ "field_order": [ "naming_series", "report_date", + "status", "column_break_4", "inspection_type", "reference_type", @@ -20,17 +22,16 @@ "column_break1", "item_name", "description", - "status", + "bom_no", + "specification_details", + "quality_inspection_template", + "readings", "section_break_14", "inspected_by", "verified_by", - "bom_no", "column_break_17", "remarks", - "amended_from", - "specification_details", - "quality_inspection_template", - "readings" + "amended_from" ], "fields": [ { @@ -231,7 +232,8 @@ "icon": "fa fa-search", "idx": 1, "is_submittable": 1, - "modified": "2019-07-12 12:07:23.153698", + "links": [], + "modified": "2020-04-26 17:50:25.068222", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index be2dd526a65..18d68539daa 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -732,11 +732,15 @@ class StockEntry(StockController): pro_doc = frappe.get_doc("Work Order", self.work_order) _validate_work_order(pro_doc) pro_doc.run_method("update_status") + if self.fg_completed_qty: pro_doc.run_method("update_work_order_qty") if self.purpose == "Manufacture": pro_doc.run_method("update_planned_qty") + if not pro_doc.operations: + pro_doc.set_actual_dates() + def get_item_details(self, args=None, for_update=False): item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, i.has_batch_no, i.sample_quantity, i.has_serial_no, From 366bce678f0b3754dfa00202530f135178fc78a9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 6 May 2020 02:38:27 +0530 Subject: [PATCH 179/410] added onboarding for manufacturing --- erpnext/manufacturing/dashboard_fixtures.py | 216 +++++++++++++++ .../manufacturing/manufacturing.json | 43 ++- .../doctype/work_order/work_order.json | 2 +- .../manufacturing/manufacturing.json | 54 ++++ .../create_bom/create_bom.json | 16 ++ .../create_product/create_product.json | 16 ++ .../introduction_to_manufacturing.json | 17 ++ .../onboarding_step/operation/operation.json | 15 ++ .../onboarding_step/warehouse/warehouse.json | 15 ++ .../work_order/work_order.json | 15 ++ .../workstation/workstation.json | 15 ++ .../downtime_analysis/downtime_analysis.py | 3 +- .../job_card_summary/job_card_summary.py | 33 +-- .../production_analytics.py | 12 +- .../quality_inspection_summary.py | 3 +- .../work_order_summary/work_order_summary.js | 7 + .../work_order_summary/work_order_summary.py | 107 +++++++- .../update_actual_start_and_end_date_in_wo.py | 5 +- .../setup_wizard/data/dashboard_charts.py | 247 ------------------ 19 files changed, 545 insertions(+), 296 deletions(-) create mode 100644 erpnext/manufacturing/dashboard_fixtures.py create mode 100644 erpnext/manufacturing/onboarding/manufacturing/manufacturing.json create mode 100644 erpnext/manufacturing/onboarding_step/create_bom/create_bom.json create mode 100644 erpnext/manufacturing/onboarding_step/create_product/create_product.json create mode 100644 erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json create mode 100644 erpnext/manufacturing/onboarding_step/operation/operation.json create mode 100644 erpnext/manufacturing/onboarding_step/warehouse/warehouse.json create mode 100644 erpnext/manufacturing/onboarding_step/work_order/work_order.json create mode 100644 erpnext/manufacturing/onboarding_step/workstation/workstation.json delete mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py new file mode 100644 index 00000000000..de3775f52c6 --- /dev/null +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -0,0 +1,216 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, erpnext, json +from frappe import _ + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "Manufacturing Dashboard", + "dashboard_name": "Manufacturing Dashboard", + "charts": [ + { "chart": "Produced Quantity", "width": "Half" }, + { "chart": "Completed Operation", "width": "Half" }, + { "chart": "Work Order Analysis", "width": "Half" }, + { "chart": "Quality Inspection Analysis", "width": "Half" }, + { "chart": "Pending Work Order", "width": "Half" }, + { "chart": "Last Month Downtime Analysis", "width": "Half" }, + { "chart": "Work Order Qty Analysis", "width": "Full" }, + { "chart": "Job Card Analysis", "width": "Full" } + ], + "cards": [ + { "card": "Total Work Order" }, + { "card": "Completed Work Order" }, + { "card": "Ongoing Job Card" }, + { "card": "Total Quality Inspection"} + ] + }] + +def get_charts(): + company = erpnext.get_default_company() + + if not company: + company = frappe.db.get_value("Company", {"is_group": 0}, "name") + + return [{ + "doctype": "Dashboard Chart", + "based_on": "creation", + "time_interval": "Yearly", + "chart_type": "Sum", + "chart_name": "Produced Quantity", + "document_type": "Work Order", + "filters_json": json.dumps({}), + "group_by_type": "Count", + "time_interval": "Quarterly", + "timespan": "Last Year", + "owner": "Administrator", + "type": "Line", + "value_based_on": "qty", + "is_public": 1, + "timeseries": 1 + }, { + "doctype": "Dashboard Chart", + "based_on": "creation", + "time_interval": "Yearly", + "chart_type": "Sum", + "chart_name": "Completed Operation", + "document_type": "Work Order Operation", + "filters_json": json.dumps({}), + "group_by_type": "Count", + "time_interval": "Quarterly", + "timespan": "Last Year", + "owner": "Administrator", + "type": "Line", + "value_based_on": "completed_qty", + "is_public": 1, + "timeseries": 1 + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Work Order Analysis", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company, "charts_based_on": "Status"}), + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Quality Inspection Analysis", + "timespan": "Last Year", + "report_name": "Quality Inspection Summary", + "owner": "Administrator", + "filters_json": json.dumps({}), + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Pending Work Order", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company, "charts_based_on": "Age"}), + "owner": "Administrator", + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Last Month Downtime Analysis", + "timespan": "Last Year", + "filters_json": json.dumps({}), + "report_name": "Downtime Analysis", + "owner": "Administrator", + "is_public": 1, + "is_custom": 1, + "type": "Bar" + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": _("Work Order Qty Analysis"), + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}), + "owner": "Administrator", + "type": "Bar", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "barOptions": { "stacked": 1 } + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Job Card Analysis", + "timespan": "Last Year", + "report_name": "Job Card Summary", + "owner": "Administrator", + "is_public": 1, + "is_custom": 1, + "filters_json": json.dumps({"company": company, "range":"Monthly"}), + "custom_options": json.dumps({ + "barOptions": { "stacked": 1 } + }), + "type": "Bar" + }] + +def get_number_cards(): + return [{ + "doctype": "Number Card", + "document_type": "Work Order", + "name": "Total Work Order", + "filters_json": json.dumps([]), + "function": "Count", + "is_public": 1, + "label": _("Total Work Order"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Work Order", + "name": "Completed Work Order", + "filters_json": json.dumps([['Work Order', 'status','=','Completed', False]]), + "function": "Count", + "is_public": 1, + "label": _("Completed Work Order"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Job Card", + "name": "Ongoing Job Card", + "filters_json": json.dumps([['Job Card', 'status','!=','Completed', False]]), + "function": "Count", + "is_public": 1, + "label": _("Ongoing Job Card"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Quality Inspection", + "name": "Total Quality Inspection", + "filters_json": json.dumps([]), + "function": "Count", + "is_public": 1, + "label": _("Total Quality Inspection"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }] \ No newline at end of file diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 0464b763c0e..f6bbf3db85c 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Order Summary\",\n \"name\": \"Work Order Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quality Inspection\"\n ],\n \"doctype\": \"Quality Inspection\",\n \"is_query_report\": true,\n \"label\": \"Quality Inspection Summary\",\n \"name\": \"Quality Inspection Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Downtime Entry\"\n ],\n \"doctype\": \"Downtime Entry\",\n \"is_query_report\": true,\n \"label\": \"Downtime Analysis\",\n \"name\": \"Downtime Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Job Card\"\n ],\n \"doctype\": \"Job Card\",\n \"is_query_report\": true,\n \"label\": \"Job Card Summary\",\n \"name\": \"Job Card Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" + "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}]" }, { "hidden": 0, @@ -32,12 +32,7 @@ } ], "category": "Domains", - "charts": [ - { - "chart_name": "Production Analysis", - "label": "Production Analysis" - } - ], + "charts": [], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -47,29 +42,59 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-04-27 00:17:26.323677", + "modified": "2020-05-14 15:06:45.963942", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", + "onboarding": "Manufacturing", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "restrict_to_domain": "Manufacturing", "shortcuts": [ { + "format": "{} Active", "label": "Item", "link_to": "Item", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, { + "format": "{} Active", "label": "BOM", "link_to": "BOM", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{\n \"is_active\": 1\n}", "type": "DocType" }, { + "format": "{} Open", "label": "Work Order", "link_to": "Work Order", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{ \"status\": \n (\n \"in\", [\"Draft\", \"Not Started\", \"In Process\"]\n )\n}", "type": "DocType" + }, + { + "format": "{} Open", + "label": "Production Plan", + "link_to": "Production Plan", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{ \"status\": \n (\n \"!=\", \"Completed\"\n )\n}", + "type": "DocType" + }, + { + "label": "Work Order Summary", + "link_to": "Work Order Summary", + "restrict_to_domain": "Manufacturing", + "type": "Report" + }, + { + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "restrict_to_domain": "Manufacturing", + "type": "Report" } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 8be22875a31..585a09db2bf 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -496,7 +496,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2020-04-24 19:32:43.323054", + "modified": "2020-05-05 19:32:43.323054", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json new file mode 100644 index 00000000000..50584e19cac --- /dev/null +++ b/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Item Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-05 16:37:08.238935", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-12 16:22:07.050224", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Manufacturing", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Manufacturing" + }, + { + "step": "Warehouse" + }, + { + "step": "Workstation" + }, + { + "step": "Operation" + }, + { + "step": "Create Product" + }, + { + "step": "Create BOM" + }, + { + "step": "Work Order" + } + ], + "subtitle": "Products, Raw Materials, BOM, Work Order and more.", + "success_message": "Manufacturing module is all setup!", + "title": "Let's Setup Manufacturing Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json new file mode 100644 index 00000000000..969c2fa5fe4 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:41:20.239696", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-05 16:41:20.239696", + "modified_by": "Administrator", + "name": "Create BOM", + "owner": "Administrator", + "reference_document": "BOM", + "title": "Create BOM" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json new file mode 100644 index 00000000000..082f31ed6e7 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:42:31.476275", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-05 16:42:31.476275", + "modified_by": "Administrator", + "name": "Create Product", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create Product" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json new file mode 100644 index 00000000000..6e5c3a9f45c --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json @@ -0,0 +1,17 @@ +{ + "action": "Update Settings", + "creation": "2020-05-05 16:40:23.676406", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-05 16:40:23.676406", + "modified_by": "Administrator", + "name": "Introduction to Manufacturing", + "owner": "Administrator", + "reference_document": "Manufacturing Settings", + "title": "Manufacturing Settings", + "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json new file mode 100644 index 00000000000..6f84db13ec8 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:15:31.706756", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:17:06.943067", + "modified_by": "Administrator", + "name": "Operation", + "owner": "Administrator", + "title": "Create Operation" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json new file mode 100644 index 00000000000..36f5204e0b6 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:13:34.014554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:16:48.345846", + "modified_by": "Administrator", + "name": "Warehouse", + "owner": "Administrator", + "title": "Create Warehouse" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json new file mode 100644 index 00000000000..57e4e7b3bc7 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:15:56.084682", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:17:33.675304", + "modified_by": "Administrator", + "name": "Work Order", + "owner": "Administrator", + "title": "Create Work Order" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json new file mode 100644 index 00000000000..6c1a06938b4 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:14:14.930214", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:16:58.808906", + "modified_by": "Administrator", + "name": "Workstation", + "owner": "Administrator", + "title": "Create Workstation" +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index d69ec189baa..dfc6b02b84f 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -47,8 +47,7 @@ def get_chart_data(data, columns): {"name": "Dataset 1", "values": datasets} ] }, - "type": "bar", - "colors": ["#ff5858"] + "type": "bar" } return chart diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index 0f60c132ec8..ae1e4f30469 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -49,33 +49,22 @@ def get_data(filters): def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) - not_start, in_progress, on_hold, completed = [], [], [] , [] + pending, completed = [], [] datasets = [] for d in labels: - not_start.append(periodic_data.get("Open").get(d)) - in_progress.append(periodic_data.get("Work In Progress").get(d)) - on_hold.append(periodic_data.get("On Hold").get(d)) + pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({'name':'Open', 'values': not_start}) - datasets.append({'name':'Work In Progress', 'values': in_progress}) - datasets.append({'name':'On Hold', 'values': on_hold}) - datasets.append({'name':'Completed', 'values': completed}) + datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Completed", "values": completed}) chart = { "data": { 'labels': labels, 'datasets': datasets }, - "type": "bar", - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"], - "axisOptions": { - "xAxisMode": "tick" - }, - "barOptions": { - "stacked": 1 - } + "type": "bar" } return chart @@ -84,9 +73,7 @@ def prepare_chart_data(job_card_details, filters): labels = [] periodic_data = { - "Open": {}, - "Work In Progress": {}, - "On Hold": {}, + "Pending": {}, "Completed": {} } @@ -100,10 +87,12 @@ def prepare_chart_data(job_card_details, filters): for d in job_card_details: if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: - if periodic_data.get(d.status) and periodic_data.get(d.status).get(period): - periodic_data[d.status][period] += 1 + status = "Completed" if d.status == "Completed" else "Pending" + + if periodic_data.get(status) and periodic_data.get(status).get(period): + periodic_data[status][period] += 1 else: - periodic_data[d.status][period] = 1 + periodic_data[status][period] = 1 return labels, periodic_data diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index f0bdfeda21c..f62cd255b5e 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -38,6 +38,7 @@ def get_columns(filters): def get_periodic_data(filters, entry): periodic_data = { + "All Work Orders": {}, "Not Started": {}, "Overdue": {}, "Pending": {}, @@ -50,6 +51,7 @@ def get_periodic_data(filters, entry): period = get_period(end_date, filters) for d in entry: if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) : + periodic_data = update_periodic_data(periodic_data, "All Work Orders", period) if d.status == 'Completed': if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date): periodic_data = update_periodic_data(periodic_data, "Completed", period) @@ -97,7 +99,7 @@ def get_data(filters, columns): periodic_data = get_periodic_data(filters,entry) - labels = ["Not Started", "Overdue", "Pending", "Completed"] + labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"] chart_data = get_chart_data(periodic_data,columns) ranges = get_period_date_ranges(filters) @@ -121,11 +123,13 @@ def get_chart_data(periodic_data, columns): datasets = [] for d in labels: + all_data.append(periodic_data.get("All Work Orders").get(d)) not_start.append(periodic_data.get("Not Started").get(d)) overdue.append(periodic_data.get("Overdue").get(d)) pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) + datasets.append({'name':'All Work Orders', 'values': all_data}) datasets.append({'name':'Not Started', 'values': not_start}) datasets.append({'name':'Overdue', 'values': overdue}) datasets.append({'name':'Pending', 'values': pending}) @@ -135,14 +139,12 @@ def get_chart_data(periodic_data, columns): "data": { 'labels': labels, 'datasets': datasets - }, - "type": "bar", - "colors": ["#5e64ff", "#ff5858", "#ffa00a", "#98d85b"] + } } + chart["type"] = "line" return chart - diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py index 9c1bfa46301..6192632bda6 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -50,8 +50,7 @@ def get_chart_data(periodic_data, columns): 'datasets': datasets }, "type": "donut", - "height": 300, - "colors": ["#ff5858", "#98d85b"] + "height": 300 } return chart diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 33b147df49a..ec9fe35d63a 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -54,5 +54,12 @@ frappe.query_reports["Work Order Summary"] = { fieldtype: "Int", default: "0" }, + { + label: __("Charts Based On"), + fieldname:"charts_based_on", + fieldtype: "Select", + options: ["Status", "Age", "Quantity"], + default: "Status" + }, ] }; diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index 92c31ab382e..bc09ed43354 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -3,8 +3,9 @@ from __future__ import unicode_literals import frappe -from frappe.utils import date_diff, today +from frappe.utils import date_diff, today, getdate, flt from frappe import _ +from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period) def execute(filters=None): columns, data = [], [] @@ -46,7 +47,15 @@ def get_data(filters): return res -def get_chart_data(periodic_data, columns): +def get_chart_data(data, filters): + if filters.get("charts_based_on") == "Status": + return get_chart_based_on_status(data) + elif filters.get("charts_based_on") == "Age": + return get_chart_based_on_age(data) + else: + return get_chart_based_on_qty(data, filters) + +def get_chart_based_on_status(data): labels = ["Not Started", "In Process", "Stopped", "Completed"] status_wise_data = { @@ -56,7 +65,7 @@ def get_chart_data(periodic_data, columns): "Completed": 0 } - for d in periodic_data: + for d in data: if d.status == "In Process" and d.produced_qty: status_wise_data["Completed"] += d.produced_qty @@ -71,12 +80,100 @@ def get_chart_data(periodic_data, columns): 'datasets': [{'name':'Qty Wise Chart', 'values': values}] }, "type": "donut", - "height": 300, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + "height": 300 } return chart +def get_chart_based_on_age(data): + labels = ["0-30 Days", "30-60 Days", "60-90 Days", "90 Above"] + + age_wise_data = { + "0-30 Days": 0, + "30-60 Days": 0, + "60-90 Days": 0, + "90 Above": 0 + } + + for d in data: + if d.age > 0 and d.age <= 30: + age_wise_data["0-30 Days"] += 1 + elif d.age > 30 and d.age <= 60: + age_wise_data["30-60 Days"] += 1 + elif d.age > 60 and d.age <= 90: + age_wise_data["60-90 Days"] += 1 + else: + age_wise_data["90 Above"] += 1 + + values = [age_wise_data["0-30 Days"], age_wise_data["30-60 Days"], + age_wise_data["60-90 Days"], age_wise_data["90 Above"]] + + chart = { + "data": { + 'labels': labels, + 'datasets': [{'name':'Qty Wise Chart', 'values': values}] + }, + "type": "donut", + "height": 300 + } + + return chart + +def get_chart_based_on_qty(data, filters): + labels, periodic_data = prepare_chart_data(data, filters) + + pending, completed = [], [] + datasets = [] + + for d in labels: + pending.append(periodic_data.get("Pending").get(d)) + completed.append(periodic_data.get("Completed").get(d)) + + datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Completed", "values": completed}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar", + "barOptions": { + "stacked": 1 + } + } + + return chart + +def prepare_chart_data(data, filters): + labels = [] + + periodic_data = { + "Pending": {}, + "Completed": {} + } + + filters.range = "Monthly" + + ranges = get_period_date_ranges(filters) + for from_date, end_date in ranges: + period = get_period(end_date, filters) + if period not in labels: + labels.append(period) + + if period not in periodic_data["Pending"]: + periodic_data["Pending"][period] = 0 + + if period not in periodic_data["Completed"]: + periodic_data["Completed"][period] = 0 + + for d in data: + if getdate(d.planned_start_date) >= from_date and getdate(d.planned_start_date) <= end_date: + periodic_data["Pending"][period] += (flt(d.qty) - flt(d.produced_qty)) + periodic_data["Completed"][period] += flt(d.produced_qty) + + return labels, periodic_data + def get_columns(filters): columns = [ { diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py index 3fab0040fb1..331c5590e58 100644 --- a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import add_to_date +from frappe.utils.dashboard import get_config, make_records def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") @@ -33,11 +34,9 @@ def execute(): planned_end_date = add_to_date(doc.planned_start_date, minutes=doc.lead_time) doc.db_set("planned_end_date", doc.actual_start_date, update_modified=False) - frappe.db.sql(""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo SET jc.production_item = wo.production_item, jc.item_name = wo.item_name WHERE jc.work_order = wo.name and IFNULL(jc.production_item, "") = "" - """) - + """) \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py deleted file mode 100644 index f5b66817672..00000000000 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ /dev/null @@ -1,247 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -import frappe -import json - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -def get_default_dashboards(): - company = frappe.get_doc("Company", get_company_for_dashboards()) - income_account = company.default_income_account or get_account("Income Account", company.name) - expense_account = company.default_expense_account or get_account("Expense Account", company.name) - bank_account = company.default_bank_account or get_account("Bank", company.name) - - return { - "Dashboards": [ - { - "doctype": "Dashboard", - "dashboard_name": "Accounts", - "charts": [ - { "chart": "Outgoing Bills (Sales Invoice)" }, - { "chart": "Incoming Bills (Purchase Invoice)" }, - { "chart": "Bank Balance" }, - { "chart": "Income" }, - { "chart": "Expenses" }, - { "chart": "Patient Appointments" } - ] - }, - { - "doctype": "Dashboard", - "dashboard_name": "Manufacturing", - "charts": [ - { "chart": "Work Order Analysis", "width": "Half" }, - { "chart": "Quality Inspection Analysis", "width": "Half" }, - { "chart": "Long Time Pending Work Orders", "width": "Half" }, - { "chart": "Downtime Analysis", "width": "Half" }, - { "chart": "Production Analysis", "width": "Full" }, - { "chart": "Job Card Analysis", "width": "Full" } - ] - } - ], - "Charts": [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Daily", - "chart_name": "Patient Appointments", - "timespan": "Last Month", - "color": "#77ecca", - "filters_json": json.dumps({}), - "chart_type": "Count", - "timeseries": 1, - "based_on": "appointment_datetime", - "owner": "Administrator", - "document_type": "Patient Appointment", - "type": "Line", - "width": "Half" - } - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Work Order Analysis", - "timespan": "Last Year", - "report_name": "Work Order Summary", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name}), - "bar": "Donut", - "custom_options": json.dumps({ - "axisOptions": { - "shortenYAxisNumbers": 1 - }, - "height": 300, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Quality Inspection Analysis", - "timespan": "Last Year", - "report_name": "Quality Inspection Summary", - "owner": "Administrator", - "filters_json": json.dumps({}), - "bar": "Donut", - "custom_options": json.dumps({ - "axisOptions": { - "shortenYAxisNumbers": 1 - }, - "height": 300, - "colors": ["#ff5858", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Long Time Pending Work Orders", - "timespan": "Last Year", - "report_name": "Work Order Summary", - "filters_json": json.dumps({"company": company.name, "age":180}), - "owner": "Administrator", - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#ff5858"], - "x_field": "name", - "y_fields": ["age"], - "y_axis_fields": [{"__islocal": "true", "idx": 1, "y_field": "age"}], - "chart_type": "Bar" - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Downtime Analysis", - "timespan": "Last Year", - "filters_json": json.dumps({}), - "report_name": "Downtime Analysis", - "owner": "Administrator", - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#ff5858"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Production Analysis", - "timespan": "Last Year", - "report_name": "Production Analytics", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#7cd6fd", "#ff5858", "#ffa00a", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Job Card Analysis", - "timespan": "Last Year", - "report_name": "Job Card Summary", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), - "bar": "Bar", - "custom_options": json.dumps({ - "axisOptions": { - "xAxisMode": "tick" - }, - "barOptions": { - "stacked": 1 - }, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] - }), - }, - ] - } - -def get_account(account_type, company): - accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) - if accounts: - return accounts[0].name From 1c9210b303df927e0b214c17d9fe1423176e682a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 14 May 2020 18:58:25 +0530 Subject: [PATCH 180/410] made changes in the dashboards, renamed number cards --- erpnext/manufacturing/dashboard_fixtures.py | 8 +++ .../manufacturing/manufacturing.json | 8 ++- .../manufacturing/manufacturing.json | 54 +++++++++++++++++++ .../create_bom/create_bom.json | 7 ++- .../create_product/create_product.json | 7 ++- .../introduction_to_manufacturing.json | 5 +- .../onboarding_step/operation/operation.json | 8 ++- .../onboarding_step/warehouse/warehouse.json | 8 ++- .../work_order/work_order.json | 8 ++- .../workstation/workstation.json | 8 ++- 10 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index de3775f52c6..3029f488dcb 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -45,6 +45,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Sum", "chart_name": "Produced Quantity", + "name": "Produced Quantity", "document_type": "Work Order", "filters_json": json.dumps({}), "group_by_type": "Count", @@ -61,6 +62,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Sum", "chart_name": "Completed Operation", + "name": "Completed Operation", "document_type": "Work Order Operation", "filters_json": json.dumps({}), "group_by_type": "Count", @@ -76,6 +78,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Work Order Analysis", + "name": "Work Order Analysis", "timespan": "Last Year", "report_name": "Work Order Summary", "owner": "Administrator", @@ -94,6 +97,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Quality Inspection Analysis", + "name": "Quality Inspection Analysis", "timespan": "Last Year", "report_name": "Quality Inspection Summary", "owner": "Administrator", @@ -112,6 +116,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Pending Work Order", + "name": "Pending Work Order", "timespan": "Last Year", "report_name": "Work Order Summary", "filters_json": json.dumps({"company": company, "charts_based_on": "Age"}), @@ -130,6 +135,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Last Month Downtime Analysis", + "name": "Last Month Downtime Analysis", "timespan": "Last Year", "filters_json": json.dumps({}), "report_name": "Downtime Analysis", @@ -142,6 +148,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": _("Work Order Qty Analysis"), + "name": _("Work Order Qty Analysis"), "timespan": "Last Year", "report_name": "Work Order Summary", "filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}), @@ -157,6 +164,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Job Card Analysis", + "name": "Job Card Analysis", "timespan": "Last Year", "report_name": "Job Card Summary", "owner": "Administrator", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index f6bbf3db85c..6288169ee2b 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -32,7 +32,11 @@ } ], "category": "Domains", - "charts": [], + "charts": [ + { + "chart_name": "Produced Quantity" + } + ], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -42,7 +46,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-14 15:06:45.963942", + "modified": "2020-05-14 18:57:26.810652", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json new file mode 100644 index 00000000000..4092e9d87cb --- /dev/null +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Item Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-05 16:37:08.238935", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 19:12:17.289867", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Manufacturing", + "owner": "Administrator", + "steps": [ + { + "step": "Warehouse" + }, + { + "step": "Workstation" + }, + { + "step": "Operation" + }, + { + "step": "Create Product" + }, + { + "step": "Create BOM" + }, + { + "step": "Work Order" + }, + { + "step": "Introduction to Manufacturing" + } + ], + "subtitle": "Products, Raw Materials, BOM, Work Order and more.", + "success_message": "Manufacturing module is all setup!", + "title": "Let's Setup Manufacturing Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json index 969c2fa5fe4..866b871f2b7 100644 --- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 1, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:41:20.239696", + "modified": "2020-05-14 19:11:57.153679", "modified_by": "Administrator", "name": "Create BOM", "owner": "Administrator", "reference_document": "BOM", - "title": "Create BOM" + "show_full_form": 1, + "title": "Create BOM (Bill of Material)", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json index 082f31ed6e7..dd2fcb4ce7f 100644 --- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 1, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:42:31.476275", + "modified": "2020-05-14 19:10:39.974370", "modified_by": "Administrator", "name": "Create Product", "owner": "Administrator", "reference_document": "Item", - "title": "Create Product" + "show_full_form": 0, + "title": "Create Product (Raw Materials / Finished Good)", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json index 6e5c3a9f45c..eb7ab3a175c 100644 --- a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json +++ b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json @@ -6,12 +6,15 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:40:23.676406", + "modified": "2020-05-14 19:11:57.152883", "modified_by": "Administrator", "name": "Introduction to Manufacturing", "owner": "Administrator", "reference_document": "Manufacturing Settings", + "show_full_form": 0, "title": "Manufacturing Settings", + "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json index 6f84db13ec8..86e62e74111 100644 --- a/erpnext/manufacturing/onboarding_step/operation/operation.json +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:17:06.943067", + "modified": "2020-05-14 19:10:27.258157", "modified_by": "Administrator", "name": "Operation", "owner": "Administrator", - "title": "Create Operation" + "reference_document": "Operation", + "show_full_form": 0, + "title": "Create Operation", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json index 36f5204e0b6..f187f57414d 100644 --- a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:16:48.345846", + "modified": "2020-05-14 19:10:07.439037", "modified_by": "Administrator", "name": "Warehouse", "owner": "Administrator", - "title": "Create Warehouse" + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Create Warehouse", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json index 57e4e7b3bc7..e757c23d122 100644 --- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:17:33.675304", + "modified": "2020-05-14 19:11:57.145924", "modified_by": "Administrator", "name": "Work Order", "owner": "Administrator", - "title": "Create Work Order" + "reference_document": "Work Order", + "show_full_form": 1, + "title": "Create Work Order", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json index 6c1a06938b4..b786443cbc7 100644 --- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:16:58.808906", + "modified": "2020-05-14 19:10:20.930139", "modified_by": "Administrator", "name": "Workstation", "owner": "Administrator", - "title": "Create Workstation" + "reference_document": "Workstation", + "show_full_form": 0, + "title": "Create Workstation / Machine", + "validate_action": 1 } \ No newline at end of file From 1ac2512c7302096a23f7a7e8a6aaf4fbf6fbd4c1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 12:55:05 +0530 Subject: [PATCH 181/410] refactor: desk page colors and filters --- erpnext/healthcare/dashboard_fixtures.py | 1 - erpnext/healthcare/desk_page/healthcare/healthcare.json | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index fc3d62f2d84..8b86ac39ace 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -28,7 +28,6 @@ def get_charts(): "name": "Patient Appointments", "chart_name": "Patient Appointments", "timespan": "Last Month", - "color": "#77ecca", "filters_json": json.dumps({}), "chart_type": "Count", "timeseries": 1, diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 5cf09b34b24..4559606a24c 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,7 +63,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-04-25 22:31:36.576444", + "modified": "2020-05-18 12:41:04.288871", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -73,13 +73,15 @@ "restrict_to_domain": "Healthcare", "shortcuts": [ { + "color": "#ffe8cd", "format": "{} Open", "label": "Patient Appointment", "link_to": "Patient Appointment", - "stats_filter": "{\n \"status\": \"Open\"\n}", + "stats_filter": "{\n \"status\": \"Open\",\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%']\n}", "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Active", "label": "Patient", "link_to": "Patient", @@ -87,10 +89,11 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Vacant", "label": "Healthcare Service Unit", "link_to": "Healthcare Service Unit", - "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}", + "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0,\n \"company\": [\"like\", \"%\" + frappe.defaults.get_global_default(\"company\") + \"%\"]\n}", "type": "DocType" }, { From 57bfee8ea923455bc591da169a1c6a6df003169a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 21:47:52 +0530 Subject: [PATCH 182/410] fix: dashboard changes --- erpnext/hr/dashboard_fixtures.py | 94 ++++++++++++++----- erpnext/hr/desk_page/hr/hr.json | 41 ++++---- .../leave_application/leave_application.json | 11 ++- .../human_resource/human_resource.json | 48 ++++++++++ .../create_department/create_department.json | 16 ++++ .../create_designation.json | 16 ++++ .../create_employee/create_employee.json | 16 ++++ .../create_holiday_list.json | 16 ++++ .../create_leave_allocation.json | 16 ++++ .../create_leave_application.json | 16 ++++ .../hr_settings/hr_settings.json | 16 ++++ .../employee_analytics/employee_analytics.py | 2 +- .../monthly_attendance_sheet.py | 16 ++-- 13 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 erpnext/hr/onboarding/human_resource/human_resource.json create mode 100644 erpnext/hr/onboarding_step/create_department/create_department.json create mode 100644 erpnext/hr/onboarding_step/create_designation/create_designation.json create mode 100644 erpnext/hr/onboarding_step/create_employee/create_employee.json create mode 100644 erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json create mode 100644 erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json create mode 100644 erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json create mode 100644 erpnext/hr/onboarding_step/hr_settings/hr_settings.json diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index dafacaada41..c608e2bb298 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -24,33 +24,36 @@ def get_human_resource_dashboard(): "is_default": 1, "charts": [ { "chart": "Gender Diversity Ratio", "width": "Half"}, - { "chart": "Employee Count", "width": "Half"}, + { "chart": "Job Application Status", "width": "Half"}, + { "chart": 'Designation Wise Employee Count', "width": "Half"}, + { "chart": 'Department Wise Employee Count', "width": "Half"}, + { "chart": 'Designation Wise Openings', "width": "Half"}, + { "chart": 'Department Wise Openings', "width": "Half"}, { "chart": "Outgoing Salary", "width": "Full"}, { "chart": "Attendance Count", "width": "Full"} ], "cards": [ {"card": "Total Employees"}, {"card": "New Joinees"}, - {'card': "Job Applicants"}, - {'card': "Employees Left"} + {'card': "Employees Left"}, + {'card': "Total Job Openings"}, + {'card': "Total Applicants"}, + {'card': "Short Listed Candidates"}, + {'card': "Rejected Candidates"}, + {'card': "Total Job Offered"}, ] } def get_recruitment_dashboard(): pass - # return { - # "name": "Human Resource", - # "dashboard_name": "Human Resource", - # "is_default": 1, - # "charts": [ - # ], - # "cards": [ - # ] - # } def get_charts(): company = erpnext.get_default_company() + date = frappe.utils.get_datetime() + + month_map = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov","Dec"] + if not company: company = frappe.db.get_value("Company", {"is_group": 0}, "name") @@ -60,24 +63,34 @@ def get_charts(): ] dashboard_charts.append( - get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + get_dashboards_chart_doc('Job Application Status', "Group By", "Donut",document_type = "Job Applicant", group_by_type="Count", group_by_based_on="status", filters_json = json.dumps([["Job Applicant","creation","Previous","1 month"]]), time_interval = "Monthly") ) - custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' - filters_json = json.dumps({"month":"May","year":"2020","company":company}) - dashboard_charts.append( - get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) ) - - custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" - filters_json = json.dumps({"company":company ,"parameter":"Department"}) + custom_options = '''{"type": "line", "lineOptions": {"dotSize": 6}, "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}''' + filters_json = json.dumps({"month": month_map[date.month - 1], "year": str(date.year), "company":company}) dashboard_charts.append( - get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Attendance Count', "Report", "Line",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", filters_json = filters_json, custom_options=custom_options) ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="department", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="designation", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="designation", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="department", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + return dashboard_charts def get_number_cards(): @@ -89,26 +102,53 @@ def get_number_cards(): ]) ) ] + number_cards.append( get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ - ["Employee","date_of_joining","Previous","6 months"], + ["Employee","date_of_joining","Previous","1 year"], ["Employee","status","=","Active"] - ]), - stats_time_interval = "Daily") + ]) ) + ) + number_cards.append( get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","modified","Previous","1 year"], ["Employee","status","=","Left"] ]) ) ) + number_cards.append( - get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ - ["Job Applicant","status","!=","Rejected"] + get_number_cards_doc("Job Applicant", "Total Applicants", filters_json = json.dumps([ + ["Job Applicant","creation","Previous","1 month"] ]) ) ) + number_cards.append( + get_number_cards_doc("Job Opening", "Total Job Openings", func = "Sum",aggregate_function_based_on = "planned_vacancies", filters_json = json.dumps([["Job Opening","creation","Previous","1 month"]]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Short Listed Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Accepted"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Rejected Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Rejected"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Offer", "Total Job Offered", filters_json = json.dumps([["Job Offer","creation","Previous","1 month"]]) + ) + ) + return number_cards @@ -125,6 +165,7 @@ def get_number_cards_doc(document_type, label, **args): "show_percentage_stats": args.show_percentage_stats or 1, "stats_time_interval": args.stats_time_interval or 'Monthly', "filters_json": args.filters_json or '[]', + "aggregate_function_based_on": args.aggregate_function_based_on or None } def get_dashboards_chart_doc(name, chart_type, graph_type, **args): @@ -151,4 +192,5 @@ def get_dashboards_chart_doc(name, chart_type, graph_type, **args): "type": graph_type, "custom_options": args.custom_options or '', "doctype": "Dashboard Chart", + "aggregate_function_based_on": args.aggregate_function_based_on or None } \ No newline at end of file diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 22aa170744c..570596eb5b9 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -77,21 +77,26 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], "creation": "2020-03-02 15:48:58.322521", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-04-29 20:29:22.114309", + "modified": "2020-05-14 12:14:21.682026", "modified_by": "Administrator", "module": "HR", "name": "HR", + "onboarding": "Human Resource", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -104,10 +109,20 @@ "type": "DocType" }, { - "format": "{} Unpaid", - "label": "Expense Claim", - "link_to": "Expense Claim", - "stats_filter": "{\"approval_status\":\"Draft\"}", + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, + { + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", + "type": "DocType" + }, + { + "label": "Salary Structure", + "link_to": "Payroll Entry", "type": "DocType" }, { @@ -117,19 +132,9 @@ "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, - { - "label": "Salary Structure", - "link_to": "Salary Structure", - "type": "DocType" - }, - { - "label": "Leave Application", - "link_to": "Leave Application", - "type": "DocType" - }, { "label": "Salary Register", - "link_to": "Salary Register", + "link_to": "Monthly Attendance Sheet", "type": "Report" } ] diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 74707a24b86..7f50ace7663 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -174,7 +174,8 @@ "label": "Status", "no_copy": 1, "options": "Open\nApproved\nRejected\nCancelled", - "permlevel": 1 + "permlevel": 1, + "reqd": 1 }, { "fieldname": "sb10", @@ -189,14 +190,14 @@ "reqd": 1 }, { + "fetch_from": "employee.company", "fieldname": "company", "fieldtype": "Link", "label": "Company", "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1, - "fetch_from": "employee.company" + "reqd": 1 }, { "allow_on_submit": 1, @@ -249,7 +250,7 @@ "is_submittable": 1, "links": [], "max_attachments": 3, - "modified": "2020-03-10 22:40:43.487721", + "modified": "2020-05-18 13:00:41.577327", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", @@ -334,4 +335,4 @@ "sort_order": "DESC", "timeline_field": "employee", "title_field": "employee_name" -} +} \ No newline at end of file diff --git a/erpnext/hr/onboarding/human_resource/human_resource.json b/erpnext/hr/onboarding/human_resource/human_resource.json new file mode 100644 index 00000000000..45dd50d8bb0 --- /dev/null +++ b/erpnext/hr/onboarding/human_resource/human_resource.json @@ -0,0 +1,48 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-05-14 11:51:45.050242", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 14:44:09.069921", + "modified_by": "Administrator", + "module": "HR", + "name": "Human Resource", + "owner": "Administrator", + "steps": [ + { + "step": "Create Department" + }, + { + "step": "Create Designation" + }, + { + "step": "Create Holiday list" + }, + { + "step": "Create Employee" + }, + { + "step": "Create Leave Allocation" + }, + { + "step": "Create Leave Application" + }, + { + "step": "HR Settings" + } + ], + "subtitle": "Employee, Recruitment, Payroll and more.", + "success_message": "The HR Module is all set up!", + "title": "Let's Setup the Human Resource Module. ", + "user_can_dismiss": 0 +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_department/create_department.json b/erpnext/hr/onboarding_step/create_department/create_department.json new file mode 100644 index 00000000000..c3e461550a4 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_department/create_department.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:44:34.682115", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:26.448420", + "modified_by": "Administrator", + "name": "Create Department", + "owner": "Administrator", + "reference_document": "Department", + "title": "Create Department" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_designation/create_designation.json b/erpnext/hr/onboarding_step/create_designation/create_designation.json new file mode 100644 index 00000000000..b58c06c3a38 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_designation/create_designation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:45:07.514193", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:41.500795", + "modified_by": "Administrator", + "name": "Create Designation", + "owner": "Administrator", + "reference_document": "Designation", + "title": "Create Designation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_employee/create_employee.json b/erpnext/hr/onboarding_step/create_employee/create_employee.json new file mode 100644 index 00000000000..b4988a5ce54 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "title": "Create Employee" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json new file mode 100644 index 00000000000..0db67d67639 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:47:34.700174", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:25:38.068582", + "modified_by": "Administrator", + "name": "Create Holiday list", + "owner": "Administrator", + "reference_document": "Holiday List", + "title": "Create Holiday list" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json new file mode 100644 index 00000000000..287dd6c0a6f --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:48:56.123718", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:48:56.123718", + "modified_by": "Administrator", + "name": "Create Leave Allocation", + "owner": "Administrator", + "reference_document": "Leave Allocation", + "title": "Create Leave Allocation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json new file mode 100644 index 00000000000..7b70a7f5bd8 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:49:45.400764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:49:45.400764", + "modified_by": "Administrator", + "name": "Create Leave Application", + "owner": "Administrator", + "reference_document": "Leave Application", + "title": "Create Leave Application" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json new file mode 100644 index 00000000000..1f4f1bfe002 --- /dev/null +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-14 13:13:52.427711", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 13:13:52.427711", + "modified_by": "Administrator", + "name": "HR Settings", + "owner": "Administrator", + "reference_document": "HR Settings", + "title": "HR settings" +} \ No newline at end of file diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index 6f8f161c3db..df64006c1be 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -65,7 +65,7 @@ def get_chart_data(parameters,employees, filters): total_employee = frappe.db.count('Employee', {'status':'Active'}) others = total_employee - sum(values) - label.append(["Others"]) + label.append(["Not Set"]) values.append(others) chart = { 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 a6509766c7c..096e3c58101 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -30,6 +30,9 @@ day_abbr = [ def execute(filters=None): if not filters: filters = {} + if filters.hide_year_field == 1: + filters.year = 2020 + conditions, filters = get_conditions(filters) columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) @@ -75,14 +78,11 @@ def execute(filters=None): return columns, data, None, chart_data def get_chart_data(emp_att_map, days): - from pprint import pprint - pprint(emp_att_map) labels = [] datasets = [ {"name": "Absent", "values": []}, {"name": "Present", "values": []}, {"name": "Leave", "values": []}, - {"name": "Unmarked", "values": []} ] for idx, day in enumerate(days, start=0): p = day.replace("::65", "") @@ -91,7 +91,6 @@ def get_chart_data(emp_att_map, days): total_leave_on_day = 0 total_present_on_day = 0 total_holiday = 0 - total_unmarked_on_day = 0 for emp in emp_att_map.keys(): if emp_att_map[emp][idx]: if emp_att_map[emp][idx] == "A": @@ -104,12 +103,10 @@ def get_chart_data(emp_att_map, days): if emp_att_map[emp][idx] == "L": total_leave_on_day += 1 - total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day datasets[0]["values"].append(total_absent_on_day) datasets[1]["values"].append(total_present_on_day) datasets[2]["values"].append(total_leave_on_day) - datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) chart = { @@ -119,10 +116,11 @@ def get_chart_data(emp_att_map, days): } } - chart["type"] = "bar" - # chart["spaceRatio"] = 0.1 - + chart["type"] = "line" + chart['lineOptions'] = { + "dotSize": 6 + } return chart From be750096ed782bd03108f3a35bd203b171bc516d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 18 May 2020 14:23:30 +0530 Subject: [PATCH 183/410] fix: bom incorrect price list rate for raw material if price list currency is different from company currency (#21585) * fix: bom incorrect price list rate for raw material if price list currency is different from company currency * fixed test cases * fixed base_rate calculation and added plc_conversion_rate trigger --- erpnext/manufacturing/doctype/bom/bom.js | 21 ++++++++- erpnext/manufacturing/doctype/bom/bom.json | 44 ++++++++++++++----- erpnext/manufacturing/doctype/bom/bom.py | 20 +++++++-- erpnext/manufacturing/doctype/bom/test_bom.py | 8 ++-- erpnext/patches.txt | 1 + .../update_price_list_currency_in_bom.py | 31 +++++++++++++ 6 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v12_0/update_price_list_currency_in_bom.py diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index ebfb7626407..44da9cae355 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -212,6 +212,12 @@ frappe.ui.form.on("BOM", { }); }, + rm_cost_as_per: function(frm) { + if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) { + frm.set_value("plc_conversion_rate", 1.0); + } + }, + routing: function(frm) { if (frm.doc.routing) { frappe.call({ @@ -242,7 +248,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ item_code: function(doc, cdt, cdn){ var scrap_items = false; var child = locals[cdt][cdn]; - if(child.doctype == 'BOM Scrap Item') { + if (child.doctype == 'BOM Scrap Item') { scrap_items = true; } @@ -252,8 +258,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ get_bom_material_detail(doc, cdt, cdn, scrap_items); }, + + buying_price_list: function(doc) { + this.apply_price_list(); + }, + + plc_conversion_rate: function(doc) { + if (!this.in_apply_price_list) { + this.apply_price_list(); + } + }, + conversion_factor: function(doc, cdt, cdn) { - if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 63f4f977c59..4ce0ecf3f27 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2013-01-22 15:11:38", "doctype": "DocType", @@ -6,23 +7,25 @@ "engine": "InnoDB", "field_order": [ "item", - "quantity", - "set_rate_of_sub_assembly_item_based_on_bom", + "company", + "item_name", + "uom", "cb0", "is_active", "is_default", "allow_alternative_item", - "image", - "item_name", - "uom", - "currency_detail", - "company", + "set_rate_of_sub_assembly_item_based_on_bom", "project", + "quantity", + "image", + "currency_detail", + "currency", "conversion_rate", "column_break_12", - "currency", "rm_cost_as_per", "buying_price_list", + "price_list_currency", + "plc_conversion_rate", "section_break_21", "with_operations", "column_break_23", @@ -176,7 +179,8 @@ }, { "fieldname": "currency_detail", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Currency and Price List" }, { "fieldname": "company", @@ -324,7 +328,7 @@ }, { "fieldname": "base_scrap_material_cost", - "fieldtype": "Data", + "fieldtype": "Currency", "label": "Scrap Material Cost(Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -477,13 +481,31 @@ { "fieldname": "column_break_52", "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-11-22 14:35:12.142150", + "links": [], + "modified": "2020-05-05 14:29:32.634952", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b1fc4deae91..6ac653e37a4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -70,11 +70,13 @@ class BOM(WebsiteGenerator): self.validate_main_item() self.validate_currency() self.set_conversion_rate() + self.set_plc_conversion_rate() self.validate_uom_is_interger() self.set_bom_material_details() self.validate_materials() self.validate_operations() self.calculate_cost() + self.update_cost(update_parent=False, from_child_bom=True, save=False) def get_context(self, context): context.parents = [{'name': 'boms', 'title': _('All BOMs') }] @@ -165,7 +167,7 @@ class BOM(WebsiteGenerator): 'rate' : rate, 'qty' : args.get("qty") or args.get("stock_qty") or 1, 'stock_qty' : args.get("qty") or args.get("stock_qty") or 1, - 'base_rate' : rate, + 'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1), 'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0 } @@ -226,7 +228,7 @@ class BOM(WebsiteGenerator): frappe.msgprint(_("{0} not found for item {1}") .format(self.rm_cost_as_per, arg["item_code"]), alert=True) - return flt(rate) / (self.conversion_rate or 1) + return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1) def update_cost(self, update_parent=True, from_child_bom=False, save=True): if self.docstatus == 2: @@ -243,10 +245,15 @@ class BOM(WebsiteGenerator): "stock_uom": d.stock_uom, "conversion_factor": d.conversion_factor }) + if rate: d.rate = rate d.amount = flt(d.rate) * flt(d.qty) - d.db_update() + d.base_rate = flt(d.rate) * flt(self.conversion_rate) + d.base_amount = flt(d.amount) * flt(self.conversion_rate) + + if save: + d.db_update() if self.docstatus == 1: self.flags.ignore_validate_update_after_submit = True @@ -372,6 +379,13 @@ class BOM(WebsiteGenerator): elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0: self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying") + def set_plc_conversion_rate(self): + if self.rm_cost_as_per in ["Valuation Rate", "Last Purchase Rate"]: + self.plc_conversion_rate = 1 + elif not self.plc_conversion_rate and self.price_list_currency: + self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, + self.company_currency(), args="for_buying") + def validate_materials(self): """ Validate raw material entries """ diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 45a7b935d38..3dfd03b1395 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -81,13 +81,13 @@ class TestBOM(unittest.TestCase): # test amounts in selected currency self.assertEqual(bom.operating_cost, 100) - self.assertEqual(bom.raw_material_cost, 8000) - self.assertEqual(bom.total_cost, 8100) + self.assertEqual(bom.raw_material_cost, 351.68) + self.assertEqual(bom.total_cost, 451.68) # test amounts in selected currency self.assertEqual(bom.base_operating_cost, 6000) - self.assertEqual(bom.base_raw_material_cost, 480000) - self.assertEqual(bom.base_total_cost, 486000) + self.assertEqual(bom.base_raw_material_cost, 21100.80) + self.assertEqual(bom.base_total_cost, 27100.80) def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self): frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..28cd21536f6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py new file mode 100644 index 00000000000..f5e7b947c23 --- /dev/null +++ b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py @@ -0,0 +1,31 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, flt +from erpnext.setup.utils import get_exchange_rate + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "bom") + frappe.reload_doc("manufacturing", "doctype", "bom_item") + + frappe.db.sql(""" UPDATE `tabBOM`, `tabPrice List` + SET + `tabBOM`.price_list_currency = `tabPrice List`.currency, + `tabBOM`.plc_conversion_rate = 1.0 + WHERE + `tabBOM`.buying_price_list = `tabPrice List`.name AND `tabBOM`.docstatus < 2 + AND `tabBOM`.rm_cost_as_per = 'Price List' + """) + + for d in frappe.db.sql(""" + SELECT + bom.creation, bom.name, bom.price_list_currency as currency, + company.default_currency as company_currency + FROM + `tabBOM` as bom, `tabCompany` as company + WHERE + bom.company = company.name AND bom.rm_cost_as_per = 'Price List' AND + bom.price_list_currency != company.default_currency AND bom.docstatus < 2""", as_dict=1): + plc_conversion_rate = get_exchange_rate(d.currency, + d.company_currency, getdate(d.creation), "for_buying") + + frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate) \ No newline at end of file From d94a38eb51bdf9ec20f4c6b0fb7926e6ac052740 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 May 2020 14:26:26 +0530 Subject: [PATCH 184/410] fix: make queries show searchfields (#21685) * fix: make queries show searchfields * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * Update queries.py * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * fix: make fields string for sql Co-authored-by: Nabin Hait --- erpnext/controllers/queries.py | 66 +++++++++++++++----- erpnext/selling/doctype/customer/customer.py | 21 ++++--- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5febfd6bf28..5fbc460a973 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,11 +8,14 @@ from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict from erpnext.stock.get_item_details import _get_item_tax_template +from frappe.utils import unique # searches for active employees def employee_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] - return frappe.db.sql("""select name, employee_name from `tabEmployee` + fields = get_fields("Employee", ["name", "employee_name"]) + + return frappe.db.sql("""select {fields} from `tabEmployee` where status = 'Active' and docstatus < 2 and ({key} like %(txt)s @@ -24,6 +27,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, employee_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'fcond': get_filters_cond(doctype, filters, conditions), 'mcond': get_match_cond(doctype) @@ -34,9 +38,12 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) - # searches for leads which are not converted + +# searches for leads which are not converted def lead_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql("""select name, lead_name, company_name from `tabLead` + fields = get_fields("Lead", ["name", "lead_name", "company_name"]) + + return frappe.db.sql("""select {fields} from `tabLead` where docstatus < 2 and ifnull(status, '') != 'Converted' and ({key} like %(txt)s @@ -50,6 +57,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, lead_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -59,6 +67,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for customer def customer_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] @@ -69,13 +78,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "customer_name", "customer_group", "territory"] - meta = frappe.get_meta("Customer") - searchfields = meta.get_search_fields() - searchfields = searchfields + [f for f in [searchfield or "name", "customer_name"] \ - if not f in searchfields] - fields = fields + [f for f in searchfields if not f in fields] + fields = get_fields("Customer", fields) - fields = ", ".join(fields) + searchfields = frappe.get_meta("Customer").get_search_fields() searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) return frappe.db.sql("""select {fields} from `tabCustomer` @@ -88,7 +93,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, customer_name limit %(start)s, %(page_len)s""".format(**{ - "fields": fields, + "fields": ", ".join(fields), "scond": searchfields, "mcond": get_match_cond(doctype), "fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'), @@ -99,6 +104,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for supplier def supplier_query(doctype, txt, searchfield, start, page_len, filters): supp_master_name = frappe.defaults.get_user_default("supp_master_name") @@ -106,7 +112,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "supplier_group"] else: fields = ["name", "supplier_name", "supplier_group"] - fields = ", ".join(fields) + + fields = get_fields("Supplier", fields) return frappe.db.sql("""select {field} from `tabSupplier` where docstatus < 2 @@ -119,7 +126,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, supplier_name limit %(start)s, %(page_len)s """.format(**{ - 'field': fields, + 'field': ', '.join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -129,6 +136,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + def tax_account_query(doctype, txt, searchfield, start, page_len, filters): company_currency = erpnext.get_company_currency(filters.get('company')) @@ -153,6 +161,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): return tax_accounts + def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): conditions = [] @@ -221,10 +230,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals "page_len": page_len }, as_dict=as_dict) + def bom(doctype, txt, searchfield, start, page_len, filters): conditions = [] + fields = get_fields("BOM", ["name", "item"]) - return frappe.db.sql("""select tabBOM.name, tabBOM.item + return frappe.db.sql("""select {fields} from tabBOM where tabBOM.docstatus=1 and tabBOM.is_active=1 @@ -234,6 +245,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( + fields=", ".join(fields), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'), key=searchfield), @@ -244,13 +256,16 @@ def bom(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len or 20 }) + def get_project_name(doctype, txt, searchfield, start, page_len, filters): cond = '' if filters.get('customer'): cond = """(`tabProject`.customer = %s or ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer"))) - return frappe.db.sql("""select `tabProject`.name from `tabProject` + fields = get_fields("Project", ["name"]) + + return frappe.db.sql("""select {fields} from `tabProject` where `tabProject`.status not in ("Completed", "Cancelled") and {cond} `tabProject`.name like %(txt)s {match_cond} order by @@ -258,6 +273,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): idx desc, `tabProject`.name asc limit {start}, {page_len}""".format( + fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]), cond=cond, match_cond=get_match_cond(doctype), start=start, @@ -268,8 +284,10 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): + fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) + return frappe.db.sql(""" - select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date + select %(fields)s from `tabDelivery Note` where `tabDelivery Note`.`%(key)s` like %(txt)s and `tabDelivery Note`.docstatus = 1 @@ -284,6 +302,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, ) %(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s """ % { + "fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]), "key": searchfield, "fcond": get_filters_cond(doctype, filters, []), "mcond": get_match_cond(doctype), @@ -349,6 +368,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): order by expiry_date, name desc limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) + def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list = [] @@ -371,6 +391,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "parent_account"], limit_start=start, limit_page_length=page_len, as_list=True) + def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date from `tabBlanket Order` bo, `tabBlanket Order Item` boi @@ -385,6 +406,7 @@ def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): company = frappe.db.escape(filters.get("company")) )) + @frappe.whitelist() def get_income_account(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond @@ -490,6 +512,7 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters): item_filters = [ @@ -507,6 +530,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters) ) return item_manufacturers + @frappe.whitelist() def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -520,6 +544,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -533,6 +558,7 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_tax_template(doctype, txt, searchfield, start, page_len, filters): @@ -556,3 +582,13 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters): taxes = _get_item_tax_template(args, taxes, for_validate=True) return [(d,) for d in set(taxes)] + + +def get_fields(doctype, fields=[]): + meta = frappe.get_meta(doctype) + fields.extend(meta.get_search_fields()) + + if meta.title_field and not meta.title_field.strip() in fields: + fields.insert(1, meta.title_field.strip()) + + return unique(fields) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3d172ac7a22..a6889e080d9 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -337,11 +337,15 @@ def get_loyalty_programs(doc): return lp_details def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): + from erpnext.controllers.queries import get_fields + if frappe.db.get_default("cust_master_name") == "Customer Name": fields = ["name", "customer_group", "territory"] else: fields = ["name", "customer_name", "customer_group", "territory"] + fields = get_fields("Customer", fields) + match_conditions = build_match_conditions("Customer") match_conditions = "and {}".format(match_conditions) if match_conditions else "" @@ -349,14 +353,17 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): filter_conditions = get_filters_cond(doctype, filters, []) match_conditions += "{}".format(filter_conditions) - return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2 - and (%s like %s or customer_name like %s) - {match_conditions} + return frappe.db.sql(""" + select %s + from `tabCustomer` + where docstatus < 2 + and (%s like %s or customer_name like %s) + {match_conditions} order by - case when name like %s then 0 else 1 end, - case when customer_name like %s then 0 else 1 end, - name, customer_name limit %s, %s""".format(match_conditions=match_conditions) % - (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), + case when name like %s then 0 else 1 end, + case when customer_name like %s then 0 else 1 end, + name, customer_name limit %s, %s + """.format(match_conditions=match_conditions) % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) From c734db5d4505fc08c2a55d1c5be53a10c669e8cb Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 18 May 2020 14:38:57 +0530 Subject: [PATCH 185/410] fix: Validate Filters in Sales Funnel. (#21761) * fix: Validate Filters in Sales Funnel. * fix: Style fixes --- .../selling/page/sales_funnel/sales_funnel.js | 4 ++++ .../selling/page/sales_funnel/sales_funnel.py | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 85c0cd8bf0c..e3d0a55c3a0 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -90,6 +90,10 @@ erpnext.SalesFunnel = class SalesFunnel { get_data(btn) { var me = this; + if (!this.company) { + frappe.throw(__("Please Select a Company.")); + } + const method_map = { "sales_funnel": "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", "opp_by_lead_source": "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source", diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index d62e2093c69..dba24ef5b00 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -8,14 +8,23 @@ from frappe import _ from erpnext.accounts.report.utils import convert import pandas as pd +def validate_filters(from_date, to_date, company): + if from_date and to_date and (from_date >= to_date): + frappe.throw(_("To Date must be greater than From Date")) + + if not company: + frappe.throw(_("Please Select a Company")) + @frappe.whitelist() def get_funnel_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + active_leads = frappe.db.sql("""select count(*) from `tabLead` where (date(`modified`) between %s and %s) and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0] active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact - left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' + left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0] opportunities = frappe.db.sql("""select count(*) from `tabOpportunity` @@ -38,6 +47,8 @@ def get_funnel_data(from_date, to_date, company): @frappe.whitelist() def get_opp_by_lead_source(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability', 'source']) if opportunities: @@ -68,11 +79,13 @@ def get_opp_by_lead_source(from_date, to_date, company): @frappe.whitelist() def get_pipeline_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability']) if opportunities: default_currency = frappe.get_cached_value('Global Defaults', 'None', 'default_currency') - + cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities] df = pd.DataFrame(cp_opportunities).groupby(['sales_stage'], as_index=True).agg({'compound_amount': 'sum'}).to_dict() From fb29714911490bee40d2c5b9342a3f9a4a84d46d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 16:28:11 +0530 Subject: [PATCH 186/410] feat: Healthcare Dashboard --- erpnext/healthcare/dashboard_fixtures.py | 88 +++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 8b86ac39ace..0f5728de9aa 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -16,7 +16,13 @@ def get_dashboards(): "name": "Healthcare", "dashboard_name": "Healthcare", "charts": [ - { "chart": "Patient Appointments" } + { "chart": "Patient Appointments", "width": "Full"}, + { "chart": "In-Patient Status", "width": "Half"}, + { "chart": "Clinical Procedures Status", "width": "Half"}, + { "chart": "Symptoms", "width": "Half"}, + { "chart": "Diagnoses", "width": "Half"}, + { "chart": "Department wise Patient Appointments", "width": "Full"}, + { "chart": "Lab Tests", "width": "Full"}, ] }] @@ -36,5 +42,85 @@ def get_charts(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "name": "Department wise Patient Appointments", + "chart_name": "Department wise Patient Appointments", + "chart_type": "Group By", + "document_type": "Patient Appointment", + "group_by_type": "Count", + "group_by_based_on": "department", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Full", + "color": "#5F62F6" + }, + { + "doctype": "Dashboard Chart", + "name": "Lab Tests", + "chart_name": "Lab Tests", + "chart_type": "Group By", + "document_type": "Lab Test", + "group_by_type": "Count", + "group_by_based_on": "template", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Full", + "color": "#8548EB" + }, + { + "doctype": "Dashboard Chart", + "name": "In-Patient Status", + "chart_name": "In-Patient Status", + "chart_type": "Group By", + "document_type": "Inpatient Record", + "group_by_type": "Count", + "group_by_based_on": "status", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Clinical Procedures Status", + "chart_name": "Clinical Procedure Status", + "chart_type": "Group By", + "document_type": "Clinical Procedure", + "group_by_type": "Count", + "group_by_based_on": "status", + 'is_public': 1, + "owner": "Administrator", + "type": "Pie", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Symptoms", + "chart_name": "Symptoms", + "chart_type": "Group By", + "document_type": "Patient Encounter Symptom", + "group_by_type": "Count", + "group_by_based_on": "complaint", + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Diagnoses", + "chart_name": "Diagnoses", + "chart_type": "Group By", + "document_type": "Patient Encounter Diagnosis", + "group_by_type": "Count", + "group_by_based_on": "diagnosis", + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", } ] From 9aae6f479398847b18bb231cee4ce35dc036420d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 18 May 2020 18:03:11 +0530 Subject: [PATCH 187/410] feat: accounting dashboard + desk --- erpnext/accounts/dashboard_fixtures.py | 131 +++++++++++++----- .../desk_page/accounting/accounting.json | 9 +- erpnext/patches.txt | 1 + .../selling/desk_page/selling/selling.json | 85 ------------ 4 files changed, 108 insertions(+), 118 deletions(-) delete mode 100644 erpnext/selling/desk_page/selling/selling.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 8b9eca5b2d3..214e467b5c0 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -3,9 +3,9 @@ import frappe import json -from frappe.utils import nowdate, add_months +from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.utils import get_fiscal_year, get_account_name def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -27,25 +27,28 @@ def get_data(): def get_dashboards(): return [{ "name": "Accounts Dashboard", - "dashboard_name": "Accounts", + "dashboard_name": "Accounts Dashboard", "doctype": "Dashboard", "charts": [ { "chart": "Profit and Loss" , "width": "Full"}, - { "chart": "Incoming Bills"}, - { "chart": "Outgoing Bills"}, - { "chart": "Accounts Receivable Ageing"}, - { "chart": "Accounts Payable Ageing"}, + { "chart": "Incoming Bills (Purchase Invoice)", "width": "Half"}, + { "chart": "Outgoing Bills (Sales Invoice)", "width": "Half"}, + { "chart": "Accounts Receivable Ageing", "width": "Half"}, + { "chart": "Accounts Payable Ageing", "width": "Half"}, { "chart": "Budget Variance", "width": "Full"}, - { "chart": "Bank Balance", "width": "Full"}, + { "chart": "Bank Balance", "width": "Full"} ], "cards": [ - {"card": "Total Payment Received"} + {"card": "Total Outgoing Bills"}, + {"card": "Total Incoming Bills"}, + {"card": "Total Incoming Payment"}, + {"card": "Total Outgoing Payment"} ] }] def get_charts(): company = frappe.get_doc("Company", get_company_for_dashboards()) - bank_account = company.default_bank_account or get_account("Bank", company.name) + bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) fiscal_year = get_fiscal_year(date=nowdate()) default_cost_center = company.cost_center @@ -58,50 +61,53 @@ def get_charts(): "filters_json": json.dumps({ "company": company.name, "filter_based_on": "Date Range", - "period_start_date": add_months(nowdate(), -4), - "period_end_date": nowdate(), + "period_start_date": get_date_str(fiscal_year[1]), + "period_end_date": get_date_str(fiscal_year[2]), "periodicity": "Monthly", "include_default_book_entries": 1 - }), + }), "type": "Bar", 'timeseries': 0, "chart_type": "Report", "chart_name": _("Profit and Loss"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Chart", "time_interval": "Monthly", - "name": "Incoming Bills", + "name": "Incoming Bills (Purchase Invoice)", "chart_name": _("Incoming Bills (Purchase Invoice)"), "timespan": "Last Year", "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), + "value_based_on": "base_net_total", + "filters_json": json.dumps({"docstatus": 1}), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", "owner": "Administrator", "document_type": "Purchase Invoice", "type": "Bar", - "width": "Half" + "width": "Half", + "is_public": 1 }, { "doctype": "Dashboard Chart", - "name": "Outgoing Bills", + "name": "Outgoing Bills (Sales Invoice)", "time_interval": "Monthly", "chart_name": _("Outgoing Bills (Sales Invoice)"), "timespan": "Last Year", "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), + "value_based_on": "base_net_total", + "filters_json": json.dumps({"docstatus": 1}), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", "owner": "Administrator", "document_type": "Sales Invoice", "type": "Bar", - "width": "Half" + "width": "Half", + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -121,7 +127,8 @@ def get_charts(): 'timeseries': 0, "chart_type": "Report", "chart_name": _("Accounts Receivable Ageing"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -141,7 +148,8 @@ def get_charts(): 'timeseries': 0, "chart_type": "Report", "chart_name": _("Accounts Payable Ageing"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -159,7 +167,8 @@ def get_charts(): "timeseries": 0, "chart_type": "Report", "chart_name": _("Budget Variance"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -167,29 +176,89 @@ def get_charts(): "time_interval": "Quarterly", "chart_name": "Bank Balance", "timespan": "Last Year", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "filters_json": json.dumps({ + "company": company.name, + "account": bank_account + }), "source": "Account Balance Timeline", "chart_type": "Custom", "timeseries": 1, "owner": "Administrator", "type": "Line", - "width": "Half" + "width": "Half", + "is_public": 1 }, ] def get_number_cards(): + fiscal_year = get_fiscal_year(date=nowdate()) + year_start_date = get_date_str(fiscal_year[1]) + year_end_date = get_date_str(fiscal_year[2]) return [ { "doctype": "Number Card", "document_type": "Payment Entry", - "name": "Total Payment Received", - "filters_json": json.dumps([]), - "label": _("Total Payment Received"), + "name": "Total Incoming Payment", + "filters_json": json.dumps([ + ['Payment Entry', 'docstatus', '=', 1], + ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], + ['Payment Entry', 'payment_type', '=', 'Receive'] + ]), + "label": _("Total Incoming Payment"), "function": "Sum", "aggregate_function_based_on": "base_received_amount", "is_public": 1, "is_custom": 1, "show_percentage_stats": 1, - "stats_time_interval": "Daily" + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Payment Entry", + "name": "Total Outgoing Payment", + "filters_json": json.dumps([ + ['Payment Entry', 'docstatus', '=', 1], + ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], + ['Payment Entry', 'payment_type', '=', 'Pay'] + ]), + "label": _("Total Outgoing Payment"), + "function": "Sum", + "aggregate_function_based_on": "base_paid_amount", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Sales Invoice", + "name": "Total Outgoing Bills", + "filters_json": json.dumps([ + ['Sales Invoice', 'docstatus', '=', 1], + ['Sales Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] + ]), + "label": _("Total Outgoing Bills"), + "function": "Sum", + "aggregate_function_based_on": "base_net_total", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Purchase Invoice", + "name": "Total Incoming Bills", + "filters_json": json.dumps([ + ['Purchase Invoice', 'docstatus', '=', 1], + ['Purchase Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] + ]), + "label": _("Total Incoming Bills"), + "function": "Sum", + "aggregate_function_based_on": "base_net_total", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" } ] diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 682eb8fcede..a783b1d0dbb 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -82,7 +82,12 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Profit and Loss", + "label": "Profit and Loss" + } + ], "creation": "2020-03-02 15:41:59.515192", "developer_mode_only": 0, "disable_user_customization": 0, @@ -92,7 +97,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-14 22:28:25.262409", + "modified": "2020-05-18 17:27:26.882340", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..e88e05dd30b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') \ No newline at end of file diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json deleted file mode 100644 index a20806b264e..00000000000 --- a/erpnext/selling/desk_page/selling/selling.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cards": [ - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Sales", - "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" - } - ], - "category": "Modules", - "charts": [ - { - "chart_name": "Income", - "label": "Income" - } - ], - "creation": "2020-01-28 11:49:12.092882", - "developer_mode_only": 0, - "disable_user_customization": 0, - "docstatus": 0, - "doctype": "Desk Page", - "extends_another_page": 0, - "icon": "", - "idx": 0, - "is_standard": 1, - "label": "Selling", - "modified": "2020-04-01 11:28:51.047373", - "modified_by": "Administrator", - "module": "Selling", - "name": "Selling", - "owner": "Administrator", - "pin_to_bottom": 0, - "pin_to_top": 0, - "shortcuts": [ - { - "label": "Sales Invoice", - "link_to": "Sales Invoice", - "type": "DocType" - }, - { - "label": "Sales Order", - "link_to": "Sales Order", - "type": "DocType" - }, - { - "label": "Quotation", - "link_to": "Quotation", - "type": "DocType" - }, - { - "label": "Delivery Note", - "link_to": "Delivery Note", - "type": "DocType" - }, - { - "label": "Accounts Receivable", - "link_to": "Accounts Receivable", - "type": "Report" - }, - { - "label": "Sales Register", - "link_to": "Sales Register", - "type": "Report" - } - ] -} \ No newline at end of file From 24477d5d0639f0ab824f045d66a58a81b860ab40 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 18:15:20 +0530 Subject: [PATCH 188/410] feat: added Number Cards to Healthcare Dashboard --- erpnext/healthcare/dashboard_fixtures.py | 79 ++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 0f5728de9aa..b3c6723be0f 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -9,8 +9,19 @@ def get_data(): return frappe._dict({ "dashboards": get_dashboards(), "charts": get_charts(), + "number_cards": get_number_cards(), }) +def get_company(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company = frappe.get_list("Company", limit=1) + if company: + return company.name + return None + def get_dashboards(): return [{ "name": "Healthcare", @@ -23,6 +34,12 @@ def get_dashboards(): { "chart": "Diagnoses", "width": "Half"}, { "chart": "Department wise Patient Appointments", "width": "Full"}, { "chart": "Lab Tests", "width": "Full"}, + ], + "cards": [ + { "card": "Total Patients" }, + { "card": "Total Patient Admitted" }, + { "card": "Open Appointments" }, + { "card": "Appointments to Bill" } ] }] @@ -124,3 +141,65 @@ def get_charts(): "width": "Half", } ] + +def get_number_cards(): + return [ + { + "name": "Total Patients", + "label": "Total Patients", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient", + "filters_json": json.dumps( + [["Patient","status","=","Active",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Total Patients Admitted", + "label": "Total Patients Admitted", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient", + "filters_json": json.dumps( + [["Patient","inpatient_status","=","Admitted",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Open Appointments", + "label": "Open Appointments", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient Appointment", + "filters_json": json.dumps( + [["Patient Appointment","company","=",get_company(),False], + ["Patient Appointment","status","=","Open",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Appointments to Bill", + "label": "Appointments to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient Appointment", + "filters_json": json.dumps( + [["Patient Appointment","company","=",get_company(),False], + ["Patient Appointment","invoiced","=",0,False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } + ] \ No newline at end of file From 5162faa7df623da56f87883ea29de635ef9697a4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 18:59:59 +0530 Subject: [PATCH 189/410] fix: add default filters in Dashboard Charts --- erpnext/healthcare/dashboard_fixtures.py | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index b3c6723be0f..647d91ab604 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -44,6 +44,7 @@ def get_dashboards(): }] def get_charts(): + company = get_company() return [ { "doctype": "Dashboard Chart", @@ -51,7 +52,10 @@ def get_charts(): "name": "Patient Appointments", "chart_name": "Patient Appointments", "timespan": "Last Month", - "filters_json": json.dumps({}), + "filters_json": json.dumps([ + ["Patient Appointment", "company", "=", company, False], + ["Patient Appointment", "status", "!=", "Cancelled"] + ]), "chart_type": "Count", "timeseries": 1, "based_on": "appointment_datetime", @@ -68,6 +72,10 @@ def get_charts(): "document_type": "Patient Appointment", "group_by_type": "Count", "group_by_based_on": "department", + "filters_json": json.dumps([ + ["Patient Appointment", "company", "=", company, False], + ["Patient Appointment", "status", "!=", "Cancelled"] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -82,6 +90,10 @@ def get_charts(): "document_type": "Lab Test", "group_by_type": "Count", "group_by_based_on": "template", + "filters_json": json.dumps([ + ["Lab Test", "company", "=", company, False], + ["Lab Test", "docstatus", "=", 1] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -96,6 +108,9 @@ def get_charts(): "document_type": "Inpatient Record", "group_by_type": "Count", "group_by_based_on": "status", + "filters_json": json.dumps([ + ["Inpatient Record", "company", "=", company, False] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -109,6 +124,10 @@ def get_charts(): "document_type": "Clinical Procedure", "group_by_type": "Count", "group_by_based_on": "status", + "filters_json": json.dumps([ + ["Clinical Procedure", "company", "=", company, False], + ["Clinical Procedure", "docstatus", "=", 1] + ]), 'is_public': 1, "owner": "Administrator", "type": "Pie", @@ -122,6 +141,7 @@ def get_charts(): "document_type": "Patient Encounter Symptom", "group_by_type": "Count", "group_by_based_on": "complaint", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -135,6 +155,7 @@ def get_charts(): "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", "group_by_based_on": "diagnosis", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -143,6 +164,7 @@ def get_charts(): ] def get_number_cards(): + company = get_company() return [ { "name": "Total Patients", @@ -179,7 +201,7 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Patient Appointment", "filters_json": json.dumps( - [["Patient Appointment","company","=",get_company(),False], + [["Patient Appointment","company","=",company,False], ["Patient Appointment","status","=","Open",False]] ), "is_public": 1, @@ -194,7 +216,7 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Patient Appointment", "filters_json": json.dumps( - [["Patient Appointment","company","=",get_company(),False], + [["Patient Appointment","company","=",company,False], ["Patient Appointment","invoiced","=",0,False]] ), "is_public": 1, From de3751387a27deff9f9dce21fd9aab3b5c99e125 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 19:15:08 +0530 Subject: [PATCH 190/410] refactor: Lab Tests and Clinical Procedures charts to Percentage type --- erpnext/healthcare/dashboard_fixtures.py | 27 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 647d91ab604..faa83cccf3d 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -30,10 +30,11 @@ def get_dashboards(): { "chart": "Patient Appointments", "width": "Full"}, { "chart": "In-Patient Status", "width": "Half"}, { "chart": "Clinical Procedures Status", "width": "Half"}, + { "chart": "Lab Tests", "width": "Half"}, + { "chart": "Clinical Procedures", "width": "Half"}, { "chart": "Symptoms", "width": "Half"}, { "chart": "Diagnoses", "width": "Half"}, - { "chart": "Department wise Patient Appointments", "width": "Full"}, - { "chart": "Lab Tests", "width": "Full"}, + { "chart": "Department wise Patient Appointments", "width": "Full"} ], "cards": [ { "card": "Total Patients" }, @@ -96,9 +97,25 @@ def get_charts(): ]), 'is_public': 1, "owner": "Administrator", - "type": "Bar", - "width": "Full", - "color": "#8548EB" + "type": "Percentage", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Clinical Procedures", + "chart_name": "Clinical Procedures", + "chart_type": "Group By", + "document_type": "Clinical Procedure", + "group_by_type": "Count", + "group_by_based_on": "procedure_template", + "filters_json": json.dumps([ + ["Clinical Procedure", "company", "=", company, False], + ["Clinical Procedure", "docstatus", "=", 1] + ]), + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", }, { "doctype": "Dashboard Chart", From b8db275a357e4d8f8d730e5ff177ae73ba898bab Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 01:46:48 +0530 Subject: [PATCH 191/410] feat: Department wise Patient Appointments custom chart --- .../dashboard_chart_source/__init__.py | 0 .../__init__.py | 0 .../department_wise_patient_appointments.js | 14 ++++ .../department_wise_patient_appointments.json | 13 ++++ .../department_wise_patient_appointments.py | 75 +++++++++++++++++++ erpnext/healthcare/dashboard_fixtures.py | 19 ++--- 6 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 erpnext/healthcare/dashboard_chart_source/__init__.py create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py diff --git a/erpnext/healthcare/dashboard_chart_source/__init__.py b/erpnext/healthcare/dashboard_chart_source/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js new file mode 100644 index 00000000000..dd6dc666d23 --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Department wise Patient Appointments"] = { + method: "erpnext.healthcare.dashboard_chart_source.department_wise_patient_appointments.department_wise_patient_appointments.get", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json new file mode 100644 index 00000000000..00301ef2c36 --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json @@ -0,0 +1,13 @@ +{ + "creation": "2020-05-18 19:18:42.571045", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2020-05-18 19:18:42.571045", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Department wise Patient Appointments", + "owner": "Administrator", + "source_name": "Department wise Patient Appointments", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py new file mode 100644 index 00000000000..3cd987b8b4e --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -0,0 +1,75 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils.dashboard import cache_source +from erpnext.stock.utils import get_stock_value_from_bin + +@frappe.whitelist() +@cache_source +def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None, heatmap_year = None): + if chart_name: + chart = frappe.get_doc('Dashboard Chart', chart_name) + else: + chart = frappe._dict(frappe.parse_json(chart)) + + labels, datapoints = [], [] + filters = frappe.parse_json(filters) + + data = frappe.db.get_list('Medical Department', fields=['name']) + if not filters: + filters = {} + + status = ['Open', 'Scheduled', 'Closed', 'Cancelled'] + for department in data: + filters['department'] = department.name + department['total_appointments'] = frappe.db.count('Patient Appointment', filters=filters) + + for entry in status: + filters['status'] = entry + department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters) + filters.pop('status') + + sorted_department_map = sorted(data, key = lambda i: i['total_appointments'],reverse=True) + + if len(sorted_department_map) > 10: + sorted_department_map = sorted_department_map[:10] + + labels = [] + open_appointments = [] + scheduled = [] + closed = [] + cancelled = [] + + for department in sorted_department_map: + labels.append(department.name) + open_appointments.append(department.open) + scheduled.append(department.scheduled) + closed.append(department.closed) + cancelled.append(department.cancelled) + + return { + 'labels': labels, + 'datasets': [ + { + 'name': 'Open', + 'values': open_appointments + }, + { + 'name': 'Scheduled', + 'values': scheduled + }, + { + 'name': 'Closed', + 'values': closed + }, + { + 'name': 'Cancelled', + 'values': cancelled + } + ], + 'type': 'bar' + } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index faa83cccf3d..4fb05a8f81f 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -69,19 +69,20 @@ def get_charts(): "doctype": "Dashboard Chart", "name": "Department wise Patient Appointments", "chart_name": "Department wise Patient Appointments", - "chart_type": "Group By", - "document_type": "Patient Appointment", - "group_by_type": "Count", - "group_by_based_on": "department", - "filters_json": json.dumps([ - ["Patient Appointment", "company", "=", company, False], - ["Patient Appointment", "status", "!=", "Cancelled"] - ]), + "chart_type": "Custom", + "source": "Department wise Patient Appointments", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Bar", "width": "Full", - "color": "#5F62F6" + "custom_options": json.dumps({ + "colors": ["#7CD5FA", "#5F62F6", "#7544E2", "#EE5555"], + "barOptions":{ + "stacked":1 + }, + "height": 300 + }) }, { "doctype": "Dashboard Chart", From d3d3836943304137dafc792a1cb842f0934f5779 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 11:53:28 +0530 Subject: [PATCH 192/410] style: cleaner conditions and returns --- .../connectors/woocommerce_connection.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 6d379f6ed5f..44f87e0462e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -75,14 +75,12 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name frappe.rename_doc("Customer", old_name, customer_name) billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - else: - billing_address = create_address(raw_billing_data, customer, "Billing") - shipping_address = create_address(raw_shipping_data, customer, "Shipping") - contact = create_contact(raw_billing_data, customer) - - if customer_exists: rename_address(billing_address, customer) rename_address(shipping_address, customer) + else: + create_address(raw_billing_data, customer, "Billing") + create_address(raw_shipping_data, customer, "Shipping") + create_contact(raw_billing_data, customer) def create_contact(data, customer): email = data.get("email", None) @@ -111,8 +109,6 @@ def create_contact(data, customer): contact.flags.ignore_mandatory = True contact.save() - return contact - def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -134,8 +130,6 @@ def create_address(raw_data, customer, address_type): address.flags.ignore_mandatory = True address.save() - return address - def rename_address(address, customer): old_address_title = address.name new_address_title = customer.name + "-" + address.address_type From 0799c679d42bb7d3cffdd48a0fe7bacd07453a77 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 12:07:23 +0530 Subject: [PATCH 193/410] feat: Healthcare Onboarding --- .../desk_page/healthcare/healthcare.json | 3 +- .../clinical_procedure_template.js | 35 +++++++++++++++ .../healthcare_practitioner.js | 35 +++++++++++++++ .../healthcare_settings.js | 34 ++++++++++++++ .../healthcare/healthcare.json | 45 +++++++++++++++++++ .../book_a_patient_appointment.json | 19 ++++++++ .../create_patient/create_patient.json | 19 ++++++++ .../create_practitioner.json | 19 ++++++++ .../create_practitioner_schedule.json | 19 ++++++++ .../explore_clinical_procedure_templates.json | 19 ++++++++ .../explore_healthcare_settings.json | 19 ++++++++ .../explore_lab_test_template.json | 19 ++++++++ ..._employee_for_healthcare_practitioner.json | 20 +++++++++ 13 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 erpnext/healthcare/module_onboarding/healthcare/healthcare.json create mode 100644 erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json create mode 100644 erpnext/healthcare/onboarding_step/create_patient/create_patient.json create mode 100644 erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json create mode 100644 erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json create mode 100644 erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json create mode 100644 erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json create mode 100644 erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json create mode 100644 erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 4559606a24c..81ba61b682c 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,10 +63,11 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-18 12:41:04.288871", + "modified": "2020-05-19 11:47:42.118464", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", + "onboarding": "Healthcare", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 57f4cdf3b2f..3f3d606ef81 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -144,3 +144,38 @@ cur_frm.set_query('item_code', 'items', function() { } }; }); + +frappe.tour['Clinical Procedure Template'] = [ + { + fieldname: 'template', + title: __('Template Name'), + description: __('Enter a name for the Clinical Procedure Template') + }, + { + fieldname: 'item_code', + title: __('Item Code'), + description: __('Set the Item Code which will be used for billing the Clinical Procedure.') + }, + { + fieldname: 'item_group', + title: __('Item Group'), + description: __('Select an Item Group for the Clinical Procedure Item.') + }, + { + fieldname: 'is_billable', + title: __('Clinical Procedure Rate'), + description: __('Check this if the Clinical Procedure is billable and also set the rate.') + }, + { + fieldname: 'consume_stock', + title: __('Consume Stock'), + description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') + + }, + { + fieldname: 'medical_department', + title: __('Medical Department'), + description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.') + } +]; + diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js index 4ab3b6e9c1b..fc0b24122ae 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js @@ -108,3 +108,38 @@ frappe.ui.form.on('Healthcare Practitioner', 'employee', function(frm) { }); } }); + +frappe.tour['Healthcare Practitioner'] = [ + { + fieldname: 'employee', + title: __('Employee'), + description: __('If you want to track Payroll and other HRMS operations for a Practitoner, create an Employee and link it here.') + }, + { + fieldname: 'practitioner_schedules', + title: __('Practitioner Schedules'), + description: __('Set the Practitioner Schedule you just created. This will be used while booking appointments.') + }, + { + fieldname: 'op_consulting_charge_item', + title: __('Out Patient Consulting Charge Item'), + description: __('Create a service item for Out Patient Consulting.') + }, + { + fieldname: 'inpatient_visit_charge_item', + title: __('Inpatient Visit Charge Item'), + description: __('If this Healthcare Practitioner works for the In-Patient Department, create a service item for Inpatient Visits.') + }, + { + fieldname: 'op_consulting_charge', + title: __('Out Patient Consulting Charge'), + description: __('Set the Out Patient Consulting Charge for this Practitioner.') + + }, + { + fieldname: 'inpatient_visit_charge', + title: __('Inpatient Visit Charge'), + description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.') + } +]; + diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 22fbf5019a6..310ba2e5108 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -39,3 +39,37 @@ var set_query_service_item = function(frm, service_item_field) { }; }); }; + +frappe.tour['Healthcare Settings'] = [ + { + fieldname: 'link_customer_to_patient', + title: __('Link Customer to Patient'), + description: __('If checked, a customer will be created for every Patient. Patient Invoices will be created against this Customer. You can also select existing Customer while creating a Patient. This field is checked by default.') + }, + { + fieldname: 'collect_registration_fee', + title: __('Collect Registration Fee'), + description: __('If your Healthcare facility bills registrations of Patients, you can check this and set the Registration Fee in the field below. Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.') + }, + { + fieldname: 'automate_appointment_invoicing', + title: __('Automate Appointment Invoicing'), + description: __('Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.') + }, + { + fieldname: 'healthcare_service_items', + title: __('Healthcare Service Items'), + description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + }, + { + fieldname: 'sb_in_ac', + title: __('Set up default Accounts for the Healthcare Facility'), + description: __('If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.') + + }, + { + fieldname: 'out_patient_sms_alerts', + title: __('Out Patient SMS alerts'), + description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + } +]; diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json new file mode 100644 index 00000000000..fc8e70230e2 --- /dev/null +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Healthcare Administrator" + } + ], + "creation": "2020-05-19 10:32:43.025852", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-19 11:48:08.746918", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Healthcare", + "owner": "Administrator", + "steps": [ + { + "step": "Create Patient" + }, + { + "step": "Create Practitioner" + }, + { + "step": "Create Practitioner Schedule" + }, + { + "step": "Setup Schedule and Employee for Healthcare Practitioner" + }, + { + "step": "Explore Healthcare Settings" + }, + { + "step": "Book a Patient Appointment" + }, + { + "step": "Explore Clinical Procedure Templates" + } + ], + "subtitle": "Patients, Practitioner Schedules, Appointments and more.", + "success_message": "Yayy! The Healthcare Module is all set up!", + "title": "Let's Setup the Healthcare Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json new file mode 100644 index 00000000000..5f0990931f3 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 11:39:54.975372", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:39:54.975372", + "modified_by": "Administrator", + "name": "Book a Patient Appointment", + "owner": "Administrator", + "reference_document": "Patient Appointment", + "show_full_form": 1, + "title": "Book a Patient Appointment", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json new file mode 100644 index 00000000000..3bfa144ba28 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:32:27.648902", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:32:27.648902", + "modified_by": "Administrator", + "name": "Create Patient", + "owner": "Administrator", + "reference_document": "Patient", + "show_full_form": 1, + "title": "Create Patient", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json new file mode 100644 index 00000000000..3ad60ed0949 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:39:55.728057", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:39:55.728057", + "modified_by": "Administrator", + "name": "Create Practitioner", + "owner": "Administrator", + "reference_document": "Healthcare Practitioner", + "show_full_form": 0, + "title": "Create Practitioner", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json new file mode 100644 index 00000000000..361921710a3 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:41:19.065753", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:41:19.065753", + "modified_by": "Administrator", + "name": "Create Practitioner Schedule", + "owner": "Administrator", + "reference_document": "Practitioner Schedule", + "show_full_form": 1, + "title": "Create Practitioner Schedule", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json new file mode 100644 index 00000000000..f0c0f612e18 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:40:51.963741", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:46:35.085270", + "modified_by": "Administrator", + "name": "Explore Clinical Procedure Templates", + "owner": "Administrator", + "reference_document": "Clinical Procedure Template", + "show_full_form": 0, + "title": "Explore Clinical Procedure Templates", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json new file mode 100644 index 00000000000..0bdadade6fd --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:14:33.044989", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-19 11:14:33.044989", + "modified_by": "Administrator", + "name": "Explore Healthcare Settings", + "owner": "Administrator", + "reference_document": "Healthcare Settings", + "show_full_form": 0, + "title": "Explore Healthcare Settings", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json new file mode 100644 index 00000000000..179c24ec776 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:44:35.766626", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:44:35.766626", + "modified_by": "Administrator", + "name": "Explore Lab Test Template", + "owner": "Administrator", + "reference_document": "Lab Test Template", + "show_full_form": 1, + "title": "Explore Lab Test Template", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json new file mode 100644 index 00000000000..6b78db38f73 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json @@ -0,0 +1,20 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 10:43:56.231679", + "docstatus": 0, + "doctype": "Onboarding Step", + "field": "schedule", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:43:56.231679", + "modified_by": "Administrator", + "name": "Setup Schedule and Employee for Healthcare Practitioner", + "owner": "Administrator", + "reference_document": "Healthcare Practitioner", + "show_full_form": 0, + "title": "Setup Schedule and Employee for Healthcare Practitioner", + "validate_action": 0 +} \ No newline at end of file From 84011b79d1c04d1bca4ac971e273f891af892edf Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 19 May 2020 13:00:54 +0530 Subject: [PATCH 194/410] fix: updated manufacturing onboarding and desk --- .../manufacturing/manufacturing.json | 9 +++--- .../downtime_entry/downtime_entry.json | 28 +++++++++++++------ .../manufacturing/manufacturing.json | 7 +++-- .../create_bom/create_bom.json | 4 +-- .../create_product/create_product.json | 4 +-- .../create_raw_materials.json | 19 +++++++++++++ .../explore_manufacturing_settings.json | 20 +++++++++++++ .../onboarding_step/operation/operation.json | 4 +-- .../onboarding_step/warehouse/warehouse.json | 4 +-- .../work_order/work_order.json | 4 +-- .../workstation/workstation.json | 4 +-- .../downtime_analysis/downtime_analysis.js | 8 +++--- .../downtime_analysis/downtime_analysis.py | 21 +++++++++++--- 13 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json create mode 100644 erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index c181d5d5361..e35f1fb4ead 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -43,11 +43,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 12:06:25.047481", + "modified": "2020-05-19 12:54:04.104444", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -78,7 +77,7 @@ "label": "Work Order", "link_to": "Work Order", "restrict_to_domain": "Manufacturing", - "stats_filter": "{ \"status\": \n (\n \"in\", [\"Draft\", \"Not Started\", \"In Process\"]\n )\n}", + "stats_filter": "{ \n \"status\": [\"in\", \n [\"Draft\", \"Not Started\", \"In Process\"]\n ]\n}", "type": "DocType" }, { @@ -86,7 +85,7 @@ "label": "Production Plan", "link_to": "Production Plan", "restrict_to_domain": "Manufacturing", - "stats_filter": "{ \"status\": \n (\n \"!=\", \"Completed\"\n )\n}", + "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, { diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json index 6ec088ad9e7..9acb4f05133 100644 --- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -13,7 +13,9 @@ "to_time", "downtime", "downtime_reason_section", - "reason" + "stop_reason", + "column_break_9", + "remarks" ], "fields": [ { @@ -41,12 +43,6 @@ "fieldname": "column_break_4", "fieldtype": "Column Break" }, - { - "fieldname": "reason", - "fieldtype": "Text", - "label": "Reason", - "reqd": 1 - }, { "fieldname": "operator", "fieldtype": "Link", @@ -66,10 +62,26 @@ "fieldtype": "Float", "label": "Downtime", "read_only": 1 + }, + { + "fieldname": "stop_reason", + "fieldtype": "Select", + "label": "Stop Reason", + "options": "\nExcessive machine set up time\nUnplanned machine maintenance\nOn-machine press checks\nMachine operator errors\nMachine malfunction\nElectricity down\nOther", + "reqd": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks" } ], "links": [], - "modified": "2020-04-20 17:34:51.299607", + "modified": "2020-05-19 12:59:37.358483", "modified_by": "Administrator", "module": "Manufacturing", "name": "Downtime Entry", diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json index 4092e9d87cb..952d1f0e071 100644 --- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 19:12:17.289867", + "modified": "2020-05-19 12:51:42.744570", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -37,6 +37,9 @@ { "step": "Create Product" }, + { + "step": "Create Raw Materials" + }, { "step": "Create BOM" }, @@ -44,7 +47,7 @@ "step": "Work Order" }, { - "step": "Introduction to Manufacturing" + "step": "Explore Manufacturing Settings" } ], "subtitle": "Products, Raw Materials, BOM, Work Order and more.", diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json index 866b871f2b7..84b4088f233 100644 --- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:11:57.153679", + "modified": "2020-05-19 12:51:31.315686", "modified_by": "Administrator", "name": "Create BOM", "owner": "Administrator", "reference_document": "BOM", "show_full_form": 1, - "title": "Create BOM (Bill of Material)", + "title": "Create a BOM (Bill of Material)", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json index dd2fcb4ce7f..0ffa30158b0 100644 --- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:39.974370", + "modified": "2020-05-19 12:50:59.010439", "modified_by": "Administrator", "name": "Create Product", "owner": "Administrator", "reference_document": "Item", "show_full_form": 0, - "title": "Create Product (Raw Materials / Finished Good)", + "title": "Create a Finished Good", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json new file mode 100644 index 00000000000..0764f2e44a8 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 11:53:17.295372", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:53:25.147837", + "modified_by": "Administrator", + "name": "Create Raw Materials", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create Raw Materials", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json new file mode 100644 index 00000000000..582aba40d68 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json @@ -0,0 +1,20 @@ +{ + "action": "Update Settings", + "creation": "2020-05-19 11:55:11.378374", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-19 12:12:28.145366", + "modified_by": "Administrator", + "name": "Explore Manufacturing Settings", + "owner": "Administrator", + "reference_document": "Manufacturing Settings", + "show_full_form": 0, + "title": "Explore Manufacturing Settings", + "validate_action": 0, + "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json index 86e62e74111..b532e6778c4 100644 --- a/erpnext/manufacturing/onboarding_step/operation/operation.json +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:27.258157", + "modified": "2020-05-19 12:50:41.642754", "modified_by": "Administrator", "name": "Operation", "owner": "Administrator", "reference_document": "Operation", "show_full_form": 0, - "title": "Create Operation", + "title": "Create a Operation", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json index f187f57414d..e23bd33b787 100644 --- a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:07.439037", + "modified": "2020-05-19 12:50:13.766712", "modified_by": "Administrator", "name": "Warehouse", "owner": "Administrator", "reference_document": "Warehouse", "show_full_form": 0, - "title": "Create Warehouse", + "title": "Create a Warehouse", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json index e757c23d122..c63363e7cb2 100644 --- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:11:57.145924", + "modified": "2020-05-19 12:51:38.133150", "modified_by": "Administrator", "name": "Work Order", "owner": "Administrator", "reference_document": "Work Order", "show_full_form": 1, - "title": "Create Work Order", + "title": "Create a Work Order", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json index b786443cbc7..df244bb494a 100644 --- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:20.930139", + "modified": "2020-05-19 12:50:33.938176", "modified_by": "Administrator", "name": "Workstation", "owner": "Administrator", "reference_document": "Workstation", "show_full_form": 0, - "title": "Create Workstation / Machine", + "title": "Create a Workstation / Machine", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js index e20342792f4..ff32dbed98a 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -7,15 +7,15 @@ frappe.query_reports["Downtime Analysis"] = { { label: __("From Date"), fieldname:"from_date", - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldtype: "Datetime", + default: frappe.datetime.add_months(frappe.datetime.now_datetime(), -1), reqd: 1 }, { label: __("To Date"), fieldname:"to_date", - fieldtype: "Date", - default: frappe.datetime.get_today(), + fieldtype: "Datetime", + default: frappe.datetime.now_datetime(), reqd: 1, }, { diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index dfc6b02b84f..2b2be4faa3b 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -16,7 +16,7 @@ def execute(filters=None): def get_data(filters): query_filters = {} - fields = ["workstation", "operator", "from_time", "to_time", "downtime", "reason"] + fields = ["name", "workstation", "operator", "from_time", "to_time", "downtime", "stop_reason", "remarks"] query_filters["from_time"] = (">=", filters.get("from_date")) query_filters["to_time"] = ("<=", filters.get("to_date")) @@ -54,6 +54,13 @@ def get_chart_data(data, columns): def get_columns(filters): return [ + { + "label": _("ID"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Downtime Entry", + "width": 100 + }, { "label": _("Machine"), "fieldname": "workstation", @@ -87,9 +94,15 @@ def get_columns(filters): "width": 150 }, { - "label": _("Reason"), - "fieldname": "reason", + "label": _("Stop Reason"), + "fieldname": "stop_reason", + "fieldtype": "Data", + "width": 220 + }, + { + "label": _("Remarks"), + "fieldname": "remarks", "fieldtype": "Text", - "width": 180 + "width": 100 } ] \ No newline at end of file From f472a19889a2853d716b31770539f813837a893e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 13:03:22 +0530 Subject: [PATCH 195/410] fix: mark onboarding steps as mandatory --- .../healthcare/healthcare.json | 7 ++----- .../book_a_patient_appointment.json | 19 ------------------- .../create_patient/create_patient.json | 4 ++-- .../create_practitioner.json | 6 +++--- .../create_practitioner_schedule.json | 4 ++-- .../explore_healthcare_settings.json | 4 ++-- .../explore_lab_test_template.json | 19 ------------------- ..._employee_for_healthcare_practitioner.json | 4 ++-- 8 files changed, 13 insertions(+), 54 deletions(-) delete mode 100644 erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json delete mode 100644 erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index fc8e70230e2..db35149f876 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 11:48:08.746918", + "modified": "2020-05-19 12:52:09.757729", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -31,14 +31,11 @@ { "step": "Explore Healthcare Settings" }, - { - "step": "Book a Patient Appointment" - }, { "step": "Explore Clinical Procedure Templates" } ], - "subtitle": "Patients, Practitioner Schedules, Appointments and more.", + "subtitle": "Patients, Practitioner Schedules, Settings and more.", "success_message": "Yayy! The Healthcare Module is all set up!", "title": "Let's Setup the Healthcare Module", "user_can_dismiss": 1 diff --git a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json deleted file mode 100644 index 5f0990931f3..00000000000 --- a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-19 11:39:54.975372", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-19 11:39:54.975372", - "modified_by": "Administrator", - "name": "Book a Patient Appointment", - "owner": "Administrator", - "reference_document": "Patient Appointment", - "show_full_form": 1, - "title": "Book a Patient Appointment", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json index 3bfa144ba28..77bc5bd7adf 100644 --- a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json +++ b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:32:27.648902", + "modified": "2020-05-19 12:26:24.023418", "modified_by": "Administrator", "name": "Create Patient", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json index 3ad60ed0949..614b201a58f 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json +++ b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json @@ -5,15 +5,15 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:39:55.728057", + "modified": "2020-05-19 12:27:39.851375", "modified_by": "Administrator", "name": "Create Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Practitioner", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json index 361921710a3..65980ef6687 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json +++ b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:41:19.065753", + "modified": "2020-05-19 12:27:09.437825", "modified_by": "Administrator", "name": "Create Practitioner Schedule", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json index 0bdadade6fd..2bdab69faa3 100644 --- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 11:14:33.044989", + "modified": "2020-05-19 12:26:48.682673", "modified_by": "Administrator", "name": "Explore Healthcare Settings", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json deleted file mode 100644 index 179c24ec776..00000000000 --- a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Show Form Tour", - "creation": "2020-05-19 11:44:35.766626", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-19 11:44:35.766626", - "modified_by": "Administrator", - "name": "Explore Lab Test Template", - "owner": "Administrator", - "reference_document": "Lab Test Template", - "show_full_form": 1, - "title": "Explore Lab Test Template", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json index 6b78db38f73..c5af177e347 100644 --- a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json @@ -6,10 +6,10 @@ "field": "schedule", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:43:56.231679", + "modified": "2020-05-19 12:26:42.492734", "modified_by": "Administrator", "name": "Setup Schedule and Employee for Healthcare Practitioner", "owner": "Administrator", From a5fcaf66f9e6a8a9935af5da680bf1f48e1eca9e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:05:22 +0530 Subject: [PATCH 196/410] fix: Added filters in number Cards --- erpnext/stock/dashboard_fixtures.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index db955c39c95..d1a3a146a23 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -24,7 +24,10 @@ def get_company_for_dashboards(): return None company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1).get("name") +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) def get_dashboards(): return [{ @@ -93,7 +96,7 @@ def get_charts(): "filters_json": json.dumps({ "period": "Monthly", "based_on": "Item", - "fiscal_year": fiscal_year, + "fiscal_year": fiscal_year_name, "company": company.name }), "is_custom": 1, @@ -108,14 +111,13 @@ def get_charts(): "chart_type": "Report", "custom_options": json.dumps({ "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {}, - "colors":["#5e64ff"] + "tooltipOptions": {} }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ "period": "Monthly", "based_on": "Item", - "fiscal_year": fiscal_year, + "fiscal_year": fiscal_year_name, "company": company.name, "period_based_on": "posting_date" }), @@ -151,7 +153,8 @@ def get_number_cards(): "document_type": "Purchase Receipt", "filters_json": json.dumps( [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False]] + ["Purchase Receipt","company","=", company.name, False], + ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -167,7 +170,8 @@ def get_number_cards(): "document_type": "Delivery Note", "filters_json": json.dumps( [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False]] + ["Delivery Note","status","=","To Bill",False], + ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -182,7 +186,8 @@ def get_number_cards(): "document_type": "Purchase Receipt", "filters_json": json.dumps( [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False]] + ["Purchase Receipt","company","=", company.name, False], + ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -197,7 +202,8 @@ def get_number_cards(): "document_type": "Delivery Note", "filters_json": json.dumps( [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False]] + ["Delivery Note","status","=","To Bill",False], + ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", From 64b36dba4fef5e2db71f27fbf3b0f37e889443cb Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:27:10 +0530 Subject: [PATCH 197/410] fix: Changed Form Tour order and grammar fixes. --- .../doctype/stock_settings/stock_settings.js | 22 +++++++++---------- .../stock/module_onboarding/stock/stock.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 81c60679467..6f9757274d1 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -22,20 +22,10 @@ frappe.tour['Stock Settings'] = [ title: __("Item Naming By"), description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") }, - { - fieldname: "valuation_method", - title: __("Valuation Method"), - description: __("Choose between FIFO and Moving Average Valuation Methods. Click") + "here" + __("to understand them") - }, - { - fieldname: "show_barcode_field", - title: __("Show Barcode Field"), - description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") - }, { fieldname: "default_warehouse", title: __("Default Warehouse"), - description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master:") + description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.") }, { fieldname: "allow_negative_stock", @@ -43,6 +33,16 @@ frappe.tour['Stock Settings'] = [ description: __("This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.") }, + { + fieldname: "valuation_method", + title: __("Valuation Method"), + description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") + }, + { + fieldname: "show_barcode_field", + title: __("Show Barcode Field"), + description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") + }, { fieldname: "automatically_set_serial_nos_based_on_fifo", title: __("Automatically Set Serial Nos based on FIFO"), diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 28d8f672850..5208dcbbf8e 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 04:02:23.634655", + "modified": "2020-05-19 13:13:19.157316", "modified_by": "Administrator", "module": "Stock", "name": "Stock", From 89dab1bc25c093eb915844d11c4f938cba4ad019 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 13:46:06 +0530 Subject: [PATCH 198/410] refactor: open links in new tab in form tour --- .../department_wise_patient_appointments.py | 3 +-- .../clinical_procedure_template.js | 4 ++-- .../doctype/healthcare_settings/healthcare_settings.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py index 3cd987b8b4e..ae3f340b1d6 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -16,7 +16,6 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d else: chart = frappe._dict(frappe.parse_json(chart)) - labels, datapoints = [], [] filters = frappe.parse_json(filters) data = frappe.db.get_list('Medical Department', fields=['name']) @@ -33,7 +32,7 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters) filters.pop('status') - sorted_department_map = sorted(data, key = lambda i: i['total_appointments'],reverse=True) + sorted_department_map = sorted(data, key = lambda i: i['total_appointments'], reverse=True) if len(sorted_department_map) > 10: sorted_department_map = sorted_department_map[:10] diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 3f3d606ef81..16d4540c7c5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -168,8 +168,8 @@ frappe.tour['Clinical Procedure Template'] = [ }, { fieldname: 'consume_stock', - title: __('Consume Stock'), - description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') + title: __('Allow Stock Consumption'), + description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') }, { diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 310ba2e5108..c266ba86477 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -59,7 +59,7 @@ frappe.tour['Healthcare Settings'] = [ { fieldname: 'healthcare_service_items', title: __('Healthcare Service Items'), - description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') }, { fieldname: 'sb_in_ac', @@ -70,6 +70,6 @@ frappe.tour['Healthcare Settings'] = [ { fieldname: 'out_patient_sms_alerts', title: __('Out Patient SMS alerts'), - description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') } ]; From 658f29915a2bf70ec722aa84b9cebcb8b7875da8 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:58:13 +0530 Subject: [PATCH 199/410] fix: PR and DN trend report charts, group by fixes --- .../report/delivery_note_trends/delivery_note_trends.py | 9 +++++++-- .../purchase_receipt_trends/purchase_receipt_trends.py | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index d088b0020d8..40a639bc090 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -12,17 +12,22 @@ def execute(filters=None): conditions = get_columns(filters, "Delivery Note") data = get_data(filters, conditions) - chart_data = get_chart_data(data) + chart_data = get_chart_data(data, filters) return conditions["columns"], data, None, chart_data -def get_chart_data(data): +def get_chart_data(data, filters): if not data: return [] labels, datapoints = [], [] + if filters.get("group_by"): + # consider only consolidated row + data = [row for row in data if row[0]] + if len(data) > 10: + # get top 10 if data too long data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 627c23b1051..3b8d8d2dcdb 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -12,17 +12,22 @@ def execute(filters=None): conditions = get_columns(filters, "Purchase Receipt") data = get_data(filters, conditions) - chart_data = get_chart_data(data) + chart_data = get_chart_data(data, filters) return conditions["columns"], data, None, chart_data -def get_chart_data(data): +def get_chart_data(data, filters): if not data: return [] labels, datapoints = [], [] + if filters.get("group_by"): + # consider only consolidated row + data = [row for row in data if row[0]] + if len(data) > 10: + # get top 10 if data too long data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] From 19ecde21979b7503f937c40d3a069de56a0b21af Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 19 May 2020 14:30:42 +0530 Subject: [PATCH 200/410] fix: add naming series (#21769) --- .../doctype/quality_meeting/quality_meeting.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 0849fd7aeb0..7691fe35870 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,10 +1,12 @@ { - "autoname": "format:MTNG-{date}", + "actions": [], + "autoname": "naming_series:", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "date", "cb_00", "status", @@ -53,9 +55,16 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], - "modified": "2019-07-13 19:57:40.500541", + "links": [], + "modified": "2020-05-19 13:18:59.821740", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", From df1cd0ef7084ac44d42be0bb6fd8d7b6f2d994f3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 15:38:46 +0530 Subject: [PATCH 201/410] fix: update default dashboard in Healthcare Desk Page --- .../department_wise_patient_appointments.py | 4 +--- erpnext/healthcare/desk_page/healthcare/healthcare.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py index ae3f340b1d6..062da6e4654 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -2,10 +2,8 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe, json -from frappe import _ +import frappe from frappe.utils.dashboard import cache_source -from erpnext.stock.utils import get_stock_value_from_bin @frappe.whitelist() @cache_source diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 81ba61b682c..14ad5e450a9 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,7 +63,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 11:47:42.118464", + "modified": "2020-05-19 14:05:10.520457", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", From 65c90959d1b45ae712e15c94ebda7c9684c390a3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 15:54:24 +0530 Subject: [PATCH 202/410] fix: renamed manufacturing dashboards to manufacturing (#21776) --- erpnext/accounts/dashboard_fixtures.py | 4 ++-- erpnext/manufacturing/dashboard_fixtures.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 214e467b5c0..cdd375802db 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -26,8 +26,8 @@ def get_data(): def get_dashboards(): return [{ - "name": "Accounts Dashboard", - "dashboard_name": "Accounts Dashboard", + "name": "Accounts", + "dashboard_name": "Accounts", "doctype": "Dashboard", "charts": [ { "chart": "Profit and Loss" , "width": "Full"}, diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 587a0322415..ef61f230acd 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -15,8 +15,8 @@ def get_data(): def get_dashboards(): return [{ - "name": "Manufacturing Dashboard", - "dashboard_name": "Manufacturing Dashboard", + "name": "Manufacturing", + "dashboard_name": "Manufacturing", "charts": [ { "chart": "Produced Quantity", "width": "Half" }, { "chart": "Completed Operation", "width": "Half" }, From 31f00b605210d8038e70470319d09fa43d910ad2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 15:56:47 +0530 Subject: [PATCH 203/410] refactor: rename dashboards in desk page --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ++-- .../manufacturing/desk_page/manufacturing/manufacturing.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index a783b1d0dbb..576d10c0247 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -122,8 +122,8 @@ "type": "DocType" }, { - "label": "Accounts Dashboard", - "link_to": "Accounts Dashboard", + "label": "Dashboard", + "link_to": "Accounts", "type": "Dashboard" }, { diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index e35f1fb4ead..bbc4c991df1 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -95,8 +95,8 @@ "type": "Report" }, { - "label": "Manufacturing Dashboard", - "link_to": "Manufacturing Dashboard", + "label": "Dashboard", + "link_to": "Manufacturing", "restrict_to_domain": "Manufacturing", "type": "Dashboard" } From 8caffe2c712bb7b2d7ab75e10538b9f22cf7d958 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 16:37:34 +0530 Subject: [PATCH 204/410] refactor: use text editor in issue web form (#21767) --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 0f15e4737fd..1df9fb79696 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2020-03-06 05:24:05.749664", + "modified": "2020-05-19 13:01:10.729088", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -76,7 +76,7 @@ { "allow_read_on_all_link_options": 0, "fieldname": "description", - "fieldtype": "Text", + "fieldtype": "Text Editor", "hidden": 0, "label": "Description", "max_length": 0, From 50d4bf578b584475399029f42c075e330a4e6dd3 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 17:02:47 +0530 Subject: [PATCH 205/410] fix: Date filters added to dashboards, number cards and minor fixes. --- erpnext/buying/dashboard_fixtures.py | 19 ++++++++++++------- .../module_onboarding/buying/buying.json | 2 +- .../purchase_order_trends.py | 7 +++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 291abb87b04..abb858f1f66 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -34,10 +34,10 @@ def get_dashboards(): "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Purchase Order Trends", "width": "Full"}, { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Purchase Order Trends", "width": "Full"} + { "chart": "Top Suppliers", "width": "Full"} ], "cards": [ { "card": "Purchase Orders to Receive"}, @@ -80,7 +80,8 @@ def get_charts(): "filters_json": json.dumps( [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], ["Material Request", "material_request_type", "=", "Purchase", False], - ["Material Request", "company", "=", company.name, False]] + ["Material Request", "company", "=", company.name, False], + ["Material Request", "transaction_date", "Between", [start_date,end_date], False]] ), "group_by_based_on": "status", "group_by_type": "Count", @@ -96,9 +97,11 @@ def get_charts(): "chart_type": "Report", "custom_options": json.dumps({ "type": "line", - "regionFill": 1, "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {} + "tooltipOptions": {}, + "lineOptions": { + "regionFill": 1 + } }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ @@ -144,7 +147,8 @@ def get_number_cards(): "filters_json": json.dumps( [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Sum", "is_public": 1, @@ -159,7 +163,8 @@ def get_number_cards(): "document_type": "Purchase Order", "filters_json": json.dumps( [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Count", "is_public": 1, diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 70d6cbb77a4..7de44f087fc 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 23:42:35.625736", + "modified": "2020-05-19 15:37:50.111851", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 011760dacc3..abe9af979ce 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -46,10 +46,13 @@ def get_chart_data(data, conditions, filters): "labels" : labels, "datasets" : [ { - "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "name" : _("{0}").format(filters.get("period")) + _(" Expenditure"), "values" : datapoints } ] }, - "type" : "line" + "type" : "line", + "lineOptions": { + "regionFill": 1 + } } \ No newline at end of file From 007eda71645cf77534c0302cead4bce91b0e37a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 18:53:08 +0530 Subject: [PATCH 206/410] fix: Instate Invoice not appearing in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index fd1cc58c20a..dd5bb4ae9b0 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,6 +60,9 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) for rate, items in items_based_on_rate.items(): + if inv in self.cgst_igst_invoices: + rate = rate/2 + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -118,11 +121,11 @@ class Gstr1Report(object): row.append(invoice_details.get(fieldname)) taxable_value = 0 for item_code, net_amount in self.invoice_items.get(invoice).items(): - if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): - taxable_value += abs(net_amount) - elif not self.item_tax_rate.get(invoice): - taxable_value += abs(net_amount) + if item_code in items: + if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + taxable_value += abs(net_amount) + elif not self.item_tax_rate.get(invoice): + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] @@ -196,7 +199,7 @@ class Gstr1Report(object): if d.item_code not in self.invoice_items.get(d.parent, {}): self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, sum(i.get('base_net_amount', 0) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + if i.item_code == d.item_code and i.parent == d.parent)) item_tax_rate = {} @@ -221,6 +224,8 @@ class Gstr1Report(object): self.items_based_on_tax_rate = {} self.invoice_cess = frappe._dict() + self.cgst_igst_invoices = [] + unidentified_gst_accounts = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: @@ -243,6 +248,8 @@ class Gstr1Report(object): tax_rate = tax_amounts[0] if cgst_or_sgst: tax_rate *= 2 + if parent not in self.cgst_igst_invoices: + self.cgst_igst_invoices.append(parent) rate_based_dict = self.items_based_on_tax_rate\ .setdefault(parent, {}).setdefault(tax_rate, []) From f128b1c0c56d8e66754d2138ba56ead60ce4889a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:05:42 +0530 Subject: [PATCH 207/410] refactor: changed the fieldtype from data to small text (#21782) --- .../accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- .../stock/doctype/delivery_note/delivery_note.json | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index db205891444..63c34ed2056 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -398,7 +398,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 @@ -1579,7 +1579,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-29 13:37:09.355300", + "modified": "2020-05-19 17:00:57.208696", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 9f5dee901ce..84d2057f960 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -24,10 +24,10 @@ "return_against", "customer_po_details", "po_no", - "section_break_18", - "pick_list", "column_break_17", "po_date", + "section_break_18", + "pick_list", "contact_info", "shipping_address_name", "shipping_address", @@ -296,7 +296,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", "label": "Customer PO Details" @@ -304,7 +303,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order No", "no_copy": 1, "oldfieldname": "po_no", @@ -318,7 +317,6 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", "label": "Customer's Purchase Order Date", @@ -326,7 +324,6 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "100px", - "read_only": 1, "width": "100px" }, { @@ -1256,7 +1253,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:51:41.288600", + "modified": "2020-05-19 17:03:45.880106", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 8b686a5721b16d144737f280eb9c2db3a446e70c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 19:08:30 +0530 Subject: [PATCH 208/410] feat: stock module dashboard and onboarding --- erpnext/accounts/dashboard_fixtures.py | 4 +- .../warehouse_wise_stock_value.py | 2 +- erpnext/stock/dashboard_fixtures.py | 194 +++++++----------- erpnext/stock/desk_page/stock/stock.json | 8 +- .../stock/module_onboarding/stock/stock.json | 14 +- .../create_a_purchase_receipt.json} | 10 +- .../create_a_supplier.json} | 10 +- ...oduction_to_price_list_and_item_price.json | 19 -- .../introduction_to_stock_entry.json | 4 +- .../setup_your_warehouse.json | 20 ++ .../delivery_note_trends.py | 2 +- .../purchase_receipt_trends.py | 2 +- .../stock/report/stock_ageing/stock_ageing.py | 4 +- 13 files changed, 132 insertions(+), 161 deletions(-) rename erpnext/stock/onboarding_step/{create_a_price_list/create_a_price_list.json => create_a_purchase_receipt/create_a_purchase_receipt.json} (56%) rename erpnext/stock/onboarding_step/{create_a_warehouse/create_a_warehouse.json => create_a_supplier/create_a_supplier.json} (57%) delete mode 100644 erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json create mode 100644 erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index cdd375802db..1eed5a0f9ca 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -81,7 +81,7 @@ def get_charts(): "timespan": "Last Year", "color": "#a83333", "value_based_on": "base_net_total", - "filters_json": json.dumps({"docstatus": 1}), + "filters_json": json.dumps([["Purchase Invoice", "docstatus", "=", 1]]), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", @@ -99,7 +99,7 @@ def get_charts(): "timespan": "Last Year", "color": "#7b933d", "value_based_on": "base_net_total", - "filters_json": json.dumps({"docstatus": 1}), + "filters_json": json.dumps([["Sales Invoice", "docstatus", "=", 1]]), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 05a50687524..374a34ea7ca 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -29,7 +29,7 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d if not warehouses: return [] - sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'], reverse=True) if len(sorted_warehouse_map) > 10: sorted_warehouse_map = sorted_warehouse_map[:10] diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index d1a3a146a23..0f1fd128f0b 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -3,6 +3,7 @@ import frappe import json +from frappe import _ from frappe.utils import nowdate from erpnext.accounts.utils import get_fiscal_year @@ -34,39 +35,72 @@ def get_dashboards(): "name": "Stock", "dashboard_name": "Stock", "charts": [ - { "chart": "Item Shortage Summary", "width": "Half"}, - { "chart": "Stock Ageing", "width": "Half"}, - { "chart": "Item Wise Annual Revenue", "width": "Half"}, - { "chart": "Item Wise Annual Expenditure", "width": "Half"}, - { "chart": "Warehouse wise Stock Value", "width": "Full"} + { "chart": "Warehouse wise Stock Value", "width": "Full"}, + { "chart": "Purchase Receipt Trends", "width": "Half"}, + { "chart": "Delivery Trends", "width": "Half"}, + { "chart": "Oldest Items", "width": "Half"}, + { "chart": "Item Shortage Summary", "width": "Half"} ], "cards": [ - { "card": "Purchase Receipts to Bill"}, - { "card": "Amount Payable against Receipt"}, - { "card": "Delivery Notes to Bill"}, - { "card": "Amount Receivable against Delivery"} + { "card": "Total Active Items"}, + { "card": "Total Warehouses"}, + { "card": "Total Stock Value"} ] }] def get_charts(): return [ { - "name": "Item Shortage Summary", - "chart_name": "Item Shortage Summary", - "chart_type": "Report", "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name - }), - "is_custom": 1, + "name": "Purchase Receipt Trends", + "time_interval": "Monthly", + "chart_name": _("Purchase Receipt Trends"), + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_net_total", + "filters_json": json.dumps([["Purchase Receipt", "docstatus", "=", 1]]), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Receipt", + "type": "Bar", + "width": "Half", + "is_public": 1 + }, + { + "doctype": "Dashboard Chart", + "name": "Delivery Trends", + "time_interval": "Monthly", + "chart_name": _("Delivery Trends"), + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_net_total", + "filters_json": json.dumps([["Delivery Note", "docstatus", "=", 1]]), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Delivery Note", + "type": "Bar", + "width": "Half", + "is_public": 1 + }, + { + "name": "Warehouse wise Stock Value", + "chart_name": _("Warehouse wise Stock Value"), + "chart_type": "Custom", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({}), + "is_custom": 0, "is_public": 1, "owner": "Administrator", - "report_name": "Item Shortage Report", + "source": "Warehouse wise Stock Value", "type": "Bar" }, { - "name": "Stock Ageing", - "chart_name": "Stock Ageing", + "name": "Oldest Items", + "chart_name": _("Oldest Items"), "chart_type": "Report", "custom_options": json.dumps({ "colors": ["#5e64ff"] @@ -84,127 +118,55 @@ def get_charts(): "type": "Bar" }, { - "name": "Item Wise Annual Revenue", - "chart_name": "Item Wise Annual Revenue", + "name": "Item Shortage Summary", + "chart_name": _("Item Shortage Summary"), "chart_type": "Report", - "custom_options": json.dumps({ - "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {}, - "colors":["#5e64ff"] - }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ - "period": "Monthly", - "based_on": "Item", - "fiscal_year": fiscal_year_name, "company": company.name }), "is_custom": 1, "is_public": 1, "owner": "Administrator", - "report_name": "Delivery Note Trends", - "type": "Bar" - }, - { - "name": "Item Wise Annual Expenditure", - "chart_name": "Item Wise Annual Expenditure", - "chart_type": "Report", - "custom_options": json.dumps({ - "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {} - }), - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "period": "Monthly", - "based_on": "Item", - "fiscal_year": fiscal_year_name, - "company": company.name, - "period_based_on": "posting_date" - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Purchase Receipt Trends", - "type": "Bar" - }, - { - "name": "Warehouse wise Stock Value", - "chart_name": "Warehouse wise Stock Value", - "chart_type": "Custom", - "doctype": "Dashboard Chart", - "filters_json": json.dumps({}), - "is_custom": 0, - "is_public": 1, - "owner": "Administrator", - "source": "Warehouse wise Stock Value", + "report_name": "Item Shortage Report", "type": "Bar" } - ] def get_number_cards(): return [ { - "name": "Amount Payable against Receipt", - "label": "Amount Payable against Receipt", - "function": "Sum", - "aggregate_function_based_on": "base_grand_total", - "doctype": "Number Card", - "document_type": "Purchase Receipt", - "filters_json": json.dumps( - [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False], - ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] - ), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "name": "Amount Receivable against Delivery", - "label": "Amount Receivable against Delivery", - "function": "Sum", - "aggregate_function_based_on": "base_grand_total", - "doctype": "Number Card", - "document_type": "Delivery Note", - "filters_json": json.dumps( - [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False], - ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] - ), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "name": "Purchase Receipts to Bill", - "label": "Purchase Receipts to Bill", + "name": "Total Active Items", + "label": _("Total Active Items"), "function": "Count", "doctype": "Number Card", - "document_type": "Purchase Receipt", - "filters_json": json.dumps( - [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False], - ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] - ), + "document_type": "Item", + "filters_json": json.dumps([["Item", "disabled", "=", 0]]), "is_public": 1, "owner": "Administrator", "show_percentage_stats": 1, - "stats_time_interval": "Daily" + "stats_time_interval": "Monthly" }, { - "name": "Delivery Notes to Bill", - "label": "Delivery Notes to Bill", + "name": "Total Warehouses", + "label": _("Total Warehouses"), "function": "Count", "doctype": "Number Card", - "document_type": "Delivery Note", - "filters_json": json.dumps( - [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False], - ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] - ), + "document_type": "Warehouse", + "filters_json": json.dumps([["Warehouse", "disabled", "=", 0]]), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Total Stock Value", + "label": _("Total Stock Value"), + "function": "Sum", + "aggregate_function_based_on": "stock_value", + "doctype": "Number Card", + "document_type": "Bin", + "filters_json": json.dumps([]), "is_public": 1, "owner": "Administrator", "show_percentage_stats": 1, diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 6b93449ecd4..4506664c1ed 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -54,10 +54,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-15 04:11:36.326013", + "modified": "2020-05-19 17:36:08.185652", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -112,6 +113,11 @@ "label": "Stock Balance", "link_to": "Stock Balance", "type": "Report" + }, + { + "label": "Stock Dashboard", + "link_to": "Stock", + "type": "Dashboard" } ], "shortcuts_label": "Quick Access" diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 5208dcbbf8e..de24575a140 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,21 +19,18 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 13:13:19.157316", + "modified": "2020-05-19 19:03:23.602423", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "owner": "Administrator", "steps": [ { - "step": "Create a Warehouse" + "step": "Setup your Warehouse" }, { "step": "Create a Product" }, - { - "step": "Stock Settings" - }, { "step": "Introduction to Stock Entry" }, @@ -41,10 +38,13 @@ "step": "Create a Stock Entry" }, { - "step": "Introduction to Price List and Item Price" + "step": "Create a Supplier" }, { - "step": "Create a Price List" + "step": "Create a Purchase Receipt" + }, + { + "step": "Stock Settings" } ], "subtitle": "Inventory, Warehouses, Analysis and more.", diff --git a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json similarity index 56% rename from erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json rename to erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json index ce5b5ecf86e..b7811a46df4 100644 --- a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json +++ b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-15 03:26:41.917046", + "creation": "2020-05-19 18:59:13.266713", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-15 03:33:09.305991", + "modified": "2020-05-19 18:59:13.266713", "modified_by": "Administrator", - "name": "Create a Price List", + "name": "Create a Purchase Receipt", "owner": "Administrator", - "reference_document": "Price List", + "reference_document": "Purchase Receipt", "show_full_form": 1, - "title": "Create a Price List", + "title": "Create a Purchase Receipt", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json similarity index 57% rename from erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json rename to erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 3269125efc2..7a64224bd43 100644 --- a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-12 18:00:03.027704", + "creation": "2020-05-14 22:09:10.043554", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:27:44.128737", + "modified": "2020-05-14 22:09:10.043554", "modified_by": "Administrator", - "name": "Create a Warehouse", + "name": "Create a Supplier", "owner": "Administrator", - "reference_document": "Warehouse", + "reference_document": "Supplier", "show_full_form": 0, - "title": "Setup your Purchase Warehouse", + "title": "Create a Supplier", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json deleted file mode 100644 index f75523ab298..00000000000 --- a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Watch Video", - "creation": "2020-05-15 03:26:01.386069", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-15 03:31:01.267728", - "modified_by": "Administrator", - "name": "Introduction to Price List and Item Price", - "owner": "Administrator", - "show_full_form": 0, - "title": "Let's take a brief look at Price List and Item Price", - "validate_action": 1, - "video_url": "https://www.youtube.com/watch?v=lY6hAQM1I28" -} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json index 229bcd49cf8..447611fe475 100644 --- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-15 03:30:39.606147", + "modified": "2020-05-19 18:55:41.457289", "modified_by": "Administrator", "name": "Introduction to Stock Entry", "owner": "Administrator", "show_full_form": 0, - "title": "Introduction to the backbone of Stock, Stock Entry.", + "title": "Introduction to the multi-purpose stock transaction", "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json new file mode 100644 index 00000000000..557c905bd6c --- /dev/null +++ b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-19 18:54:19.383397", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 18:54:19.383397", + "modified_by": "Administrator", + "name": "Setup your Warehouse", + "owner": "Administrator", + "path": "Tree/Warehouse", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 40a639bc090..5a931e7efac 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -40,7 +40,7 @@ def get_chart_data(data, filters): "labels" : labels, "datasets" : [ { - "name": _("Total Revenue"), + "name": _("Total Delivered Amount"), "values": datapoints } ] diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 3b8d8d2dcdb..43f1f373d35 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -40,7 +40,7 @@ def get_chart_data(data, filters): "labels" : labels, "datasets" : [ { - "name": _("Total Expenditure"), + "name": _("Total Received Amount"), "values": datapoints } ] diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index c5b8f43f968..af997801551 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -53,7 +53,7 @@ def get_average_age(fifo_queue, to_date): age_qty += batch_age * 1 total_qty += 1 - return (age_qty / total_qty) if total_qty else 0.0 + return flt(age_qty / total_qty, 2) if total_qty else 0.0 def get_columns(filters): columns = [ @@ -242,6 +242,8 @@ def get_chart_data(data, filters): if filters.get("show_warehouse_wise_stock"): return {} + data.sort(key = lambda row: row[6], reverse=True) + if len(data) > 10: data = data[:10] From aa85e511da996626014a1bf5c93917d3b87e5a37 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:51:45 +0530 Subject: [PATCH 209/410] feat: Production Planning Against Sales Order/Material Request/Work Order (#21763) Co-authored-by: Marica --- .../manufacturing/manufacturing.json | 4 +- .../report/bom_operations_time/__init__.py | 0 .../bom_operations_time.js | 9 + .../bom_operations_time.json | 28 ++ .../bom_operations_time.py | 112 ++++++ .../production_planning_report/__init__.py | 0 .../production_planning_report.js | 111 ++++++ .../production_planning_report.json | 31 ++ .../production_planning_report.py | 371 ++++++++++++++++++ erpnext/patches.txt | 1 + erpnext/patches/v12_0/update_bom_in_so_mr.py | 19 + .../doctype/sales_order/sales_order.js | 9 + .../sales_order_item/sales_order_item.json | 21 +- .../material_request/material_request.js | 11 +- .../material_request_item.json | 20 +- erpnext/stock/get_item_details.py | 3 +- 16 files changed, 741 insertions(+), 9 deletions(-) create mode 100644 erpnext/manufacturing/report/bom_operations_time/__init__.py create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py create mode 100644 erpnext/manufacturing/report/production_planning_report/__init__.py create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.js create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.json create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.py create mode 100644 erpnext/patches/v12_0/update_bom_in_so_mr.py diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index bbc4c991df1..ecd2dc9b769 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}]" + "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Planning Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Planning Report\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Operations Time\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Operations Time\"\n}]" }, { "hidden": 0, @@ -46,7 +46,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 12:54:04.104444", + "modified": "2020-05-19 14:05:59.100891", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/manufacturing/report/bom_operations_time/__init__.py b/erpnext/manufacturing/report/bom_operations_time/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js new file mode 100644 index 00000000000..7468e34020c --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js @@ -0,0 +1,9 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["BOM Operations Time"] = { + "filters": [ + + ] +}; diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json new file mode 100644 index 00000000000..665c5b9f79e --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json @@ -0,0 +1,28 @@ +{ + "add_total_row": 0, + "creation": "2020-03-03 01:41:20.862521", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-03-03 01:41:20.862521", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "BOM Operations Time", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "BOM", + "report_name": "BOM Operations Time", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing Manager" + }, + { + "role": "Manufacturing User" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py new file mode 100644 index 00000000000..1279011b222 --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py @@ -0,0 +1,112 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) + return columns, data + +def get_data(filters): + data = [] + + bom_data = [] + for d in frappe.db.sql(""" + SELECT + bom.name, bom.item, bom.item_name, bom.uom, + bomps.operation, bomps.workstation, bomps.time_in_mins + FROM `tabBOM` bom, `tabBOM Operation` bomps + WHERE + bom.docstatus = 1 and bom.is_active = 1 and bom.name = bomps.parent + """, as_dict=1): + row = get_args() + if d.name not in bom_data: + bom_data.append(d.name) + row.update(d) + else: + row.update({ + "operation": d.operation, + "workstation": d.workstation, + "time_in_mins": d.time_in_mins + }) + + data.append(row) + + used_as_subassembly_items = get_bom_count(bom_data) + + for d in data: + d.used_as_subassembly_items = used_as_subassembly_items.get(d.name, 0) + + return data + +def get_bom_count(bom_data): + data = frappe.get_all("BOM Item", + fields=["count(name) as count", "bom_no"], + filters= {"bom_no": ("in", bom_data)}, group_by = "bom_no") + + bom_count = {} + for d in data: + bom_count.setdefault(d.bom_no, d.count) + + return bom_count + +def get_args(): + return frappe._dict({ + "name": "", + "item": "", + "item_name": "", + "uom": "" + }) + +def get_columns(filters): + return [{ + "label": _("BOM ID"), + "options": "BOM", + "fieldname": "name", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("BOM Item Code"), + "options": "Item", + "fieldname": "item", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 110 + }, { + "label": _("UOM"), + "options": "UOM", + "fieldname": "uom", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("Operation"), + "options": "Operation", + "fieldname": "operation", + "fieldtype": "Link", + "width": 120 + }, { + "label": _("Workstation"), + "options": "Workstation", + "fieldname": "workstation", + "fieldtype": "Link", + "width": 110 + }, { + "label": _("Time (In Mins)"), + "fieldname": "time_in_mins", + "fieldtype": "Int", + "width": 140 + }, { + "label": _("Subassembly BOM Count"), + "fieldname": "used_as_subassembly_items", + "fieldtype": "Int", + "width": 180 + }] + + diff --git a/erpnext/manufacturing/report/production_planning_report/__init__.py b/erpnext/manufacturing/report/production_planning_report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js new file mode 100644 index 00000000000..675b8a11008 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js @@ -0,0 +1,111 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Production Planning Report"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"based_on", + "label": __("Based On"), + "fieldtype": "Select", + "options": ["Sales Order", "Material Request", "Work Order"], + "default": "Sales Order", + "reqd": 1, + on_change: function() { + let filters = frappe.query_report.filters; + let based_on = frappe.query_report.get_filter_value('based_on'); + let options = { + "Sales Order": ["Delivery Date", "Total Amount"], + "Material Request": ["Required Date"], + "Work Order": ["Planned Start Date"] + } + + filters.forEach(d => { + if (d.fieldname == "order_by") { + d.df.options = options[based_on]; + d.set_input(d.df.options) + } + }); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"docnames", + "label": __("Document Name"), + "fieldtype": "MultiSelectList", + "options": "Sales Order", + "get_data": function(txt) { + if (!frappe.query_report.filters) return; + + let based_on = frappe.query_report.get_filter_value('based_on'); + if (!based_on) return; + + return frappe.db.get_link_options(based_on, txt); + }, + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + "docstatus": 1, + "company": company + } + }; + } + }, + { + "fieldname":"raw_material_warehouse", + "label": __("Raw Material Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + "depends_on": "eval: doc.based_on != 'Work Order'", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + "company": company + } + }; + } + }, + { + "fieldname":"order_by", + "label": __("Order By"), + "fieldtype": "Select", + "options": ["Delivery Date", "Total Amount"], + "default": "Delivery Date" + }, + { + "fieldname":"include_subassembly_raw_materials", + "label": __("Include Sub-assembly Raw Materials"), + "fieldtype": "Check", + "depends_on": "eval: doc.based_on != 'Work Order'", + "default": 0 + }, + ], + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "production_item_name" && data && data.qty_to_manufacture > data.available_qty ) { + value = `
    ${value}
    `; + } + + if (column.fieldname == "production_item" && !data.name ) { + value = ""; + } + + if (column.fieldname == "raw_material_name" && data && data.required_qty > data.allotted_qty ) { + value = `
    ${value}
    `; + } + + return value; + }, +}; diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.json b/erpnext/manufacturing/report/production_planning_report/production_planning_report.json new file mode 100644 index 00000000000..f37dad39a43 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2020-03-06 11:37:43.180095", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-03-06 11:38:05.789851", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Planning Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Work Order", + "report_name": "Production Planning Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py new file mode 100644 index 00000000000..b5e6c6fc853 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -0,0 +1,371 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + +# and bom_no is not null and bom_no !='' + +mapper = { + "Sales Order": { + "fields": """ item_code as production_item, item_name as production_item_name, stock_uom, + stock_qty as qty_to_manufacture, `tabSales Order Item`.parent as name, bom_no, warehouse, + `tabSales Order Item`.delivery_date, `tabSales Order`.base_grand_total """, + "filters": """`tabSales Order Item`.docstatus = 1 and stock_qty > produced_qty + and `tabSales Order`.per_delivered < 100.0""" + }, + "Material Request": { + "fields": """ item_code as production_item, item_name as production_item_name, stock_uom, + stock_qty as qty_to_manufacture, `tabMaterial Request Item`.parent as name, bom_no, warehouse, + `tabMaterial Request Item`.schedule_date """, + "filters": """`tabMaterial Request`.docstatus = 1 and `tabMaterial Request`.per_ordered < 100 + and `tabMaterial Request`.material_request_type = 'Manufacture' """ + }, + "Work Order": { + "fields": """ production_item, item_name as production_item_name, planned_start_date, + stock_uom, qty as qty_to_manufacture, name, bom_no, fg_warehouse as warehouse """, + "filters": "docstatus = 1 and status not in ('Completed', 'Stopped')" + }, +} + +order_mapper = { + "Sales Order": { + "Delivery Date": "`tabSales Order Item`.delivery_date asc", + "Total Amount": "`tabSales Order`.base_grand_total desc" + }, + "Material Request": { + "Required Date": "`tabMaterial Request Item`.schedule_date asc" + }, + "Work Order": { + "Planned Start Date": "planned_start_date asc" + } +} + +def execute(filters=None): + return ProductionPlanReport(filters).execute_report() + +class ProductionPlanReport(object): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.raw_materials_dict = {} + self.data = [] + + def execute_report(self): + self.get_open_orders() + self.get_raw_materials() + self.get_item_details() + self.get_bin_details() + self.get_purchase_details() + self.prepare_data() + self.get_columns() + + return self.columns, self.data + + def get_open_orders(self): + doctype = ("`tabWork Order`" if self.filters.based_on == "Work Order" + else "`tab{doc}`, `tab{doc} Item`".format(doc=self.filters.based_on)) + + filters = mapper.get(self.filters.based_on)["filters"] + filters = self.prepare_other_conditions(filters, self.filters.based_on) + order_by = " ORDER BY %s" % (order_mapper[self.filters.based_on][self.filters.order_by]) + + self.orders = frappe.db.sql(""" SELECT {fields} from {doctype} + WHERE {filters} {order_by}""".format( + doctype = doctype, + filters = filters, + order_by = order_by, + fields = mapper.get(self.filters.based_on)["fields"] + ), tuple(self.filters.docnames), as_dict=1) + + def prepare_other_conditions(self, filters, doctype): + if self.filters.docnames: + field = "name" if doctype == "Work Order" else "`tab{} Item`.parent".format(doctype) + filters += " and %s in (%s)" % (field, ','.join(['%s'] * len(self.filters.docnames))) + + if doctype != "Work Order": + filters += " and `tab{doc}`.name = `tab{doc} Item`.parent".format(doc=doctype) + + if self.filters.company: + filters += " and `tab%s`.company = %s" %(doctype, frappe.db.escape(self.filters.company)) + + return filters + + def get_raw_materials(self): + if not self.orders: return + self.warehouses = [d.warehouse for d in self.orders] + self.item_codes = [d.production_item for d in self.orders] + + if self.filters.based_on == "Work Order": + work_orders = [d.name for d in self.orders] + + raw_materials = frappe.get_all("Work Order Item", + fields=["parent", "item_code", "item_name as raw_material_name", + "source_warehouse as warehouse", "required_qty"], + filters = {"docstatus": 1, "parent": ("in", work_orders), "source_warehouse": ("!=", "")}) or [] + self.warehouses.extend([d.source_warehouse for d in raw_materials]) + + else: + bom_nos = [] + + for d in self.orders: + bom_no = d.bom_no or frappe.get_cached_value("Item", d.production_item, "default_bom") + + if not d.bom_no: + d.bom_no = bom_no + + bom_nos.append(bom_no) + + bom_doctype = ("BOM Explosion Item" + if self.filters.include_subassembly_raw_materials else "BOM Item") + + qty_field = ("qty_consumed_per_unit" + if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)") + + raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code, + bom_item.item_name as raw_material_name, {0} as required_qty + FROM + `tabBOM` as bom, `tab{1}` as bom_item + WHERE + bom_item.parent in ({2}) and bom_item.parent = bom.name and bom.docstatus = 1 + """.format(qty_field, bom_doctype, ','.join(["%s"] * len(bom_nos))), tuple(bom_nos), as_dict=1) + + if not raw_materials: return + + self.item_codes.extend([d.item_code for d in raw_materials]) + + for d in raw_materials: + if d.parent not in self.raw_materials_dict: + self.raw_materials_dict.setdefault(d.parent, []) + + rows = self.raw_materials_dict[d.parent] + rows.append(d) + + def get_item_details(self): + if not (self.orders and self.item_codes): return + + self.item_details = {} + for d in frappe.get_all("Item Default", fields = ["parent", "default_warehouse"], + filters = {"company": self.filters.company, "parent": ("in", self.item_codes)}): + self.item_details[d.parent] = d + + def get_bin_details(self): + if not (self.orders and self.raw_materials_dict): return + + self.bin_details = {} + self.mrp_warehouses = [] + if self.filters.raw_material_warehouse: + self.mrp_warehouses.extend(get_child_warehouses(self.filters.raw_material_warehouse)) + self.warehouses.extend(self.mrp_warehouses) + + for d in frappe.get_all("Bin", + fields=["warehouse", "item_code", "actual_qty", "ordered_qty", "projected_qty"], + filters = {"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)}): + key = (d.item_code, d.warehouse) + if key not in self.bin_details: + self.bin_details.setdefault(key, d) + + def get_purchase_details(self): + if not (self.orders and self.raw_materials_dict): return + + self.purchase_details = {} + + for d in frappe.get_all("Purchase Order Item", + fields=["item_code", "min(schedule_date) as arrival_date", "qty as arrival_qty", "warehouse"], + filters = {"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)}, + group_by = "item_code, warehouse"): + key = (d.item_code, d.warehouse) + if key not in self.purchase_details: + self.purchase_details.setdefault(key, d) + + def prepare_data(self): + if not self.orders: return + + for d in self.orders: + key = d.name if self.filters.based_on == "Work Order" else d.bom_no + + if not self.raw_materials_dict.get(key): continue + + bin_data = self.bin_details.get((d.production_item, d.warehouse)) or {} + d.update({ + "for_warehouse": d.warehouse, + "available_qty": 0 + }) + + if bin_data and bin_data.get("actual_qty") > 0 and d.qty_to_manufacture: + d.available_qty = (bin_data.get("actual_qty") + if (d.qty_to_manufacture > bin_data.get("actual_qty")) else d.qty_to_manufacture) + + bin_data["actual_qty"] -= d.available_qty + + self.update_raw_materials(d, key) + + def update_raw_materials(self, data, key): + self.index = 0 + self.raw_materials_dict.get(key) + + warehouses = self.mrp_warehouses or [] + for d in self.raw_materials_dict.get(key): + if self.filters.based_on != "Work Order": + d.required_qty = d.required_qty * data.qty_to_manufacture + + if not warehouses: + warehouses = [data.warehouse] + + if self.filters.based_on == "Work Order" and d.warehouse: + warehouses = [d.warehouse] + else: + item_details = self.item_details.get(d.item_code) + if item_details: + warehouses = [item_details["default_warehouse"]] + + d.remaining_qty = d.required_qty + self.pick_materials_from_warehouses(d, data, warehouses) + + if (d.remaining_qty and self.filters.raw_material_warehouse + and d.remaining_qty != d.required_qty): + row = self.get_args() + d.warehouse = self.filters.raw_material_warehouse + d.required_qty = d.remaining_qty + d.allotted_qty = 0 + row.update(d) + self.data.append(row) + + def pick_materials_from_warehouses(self, args, order_data, warehouses): + for index, warehouse in enumerate(warehouses): + if not args.remaining_qty: return + + row = self.get_args() + + key = (args.item_code, warehouse) + bin_data = self.bin_details.get(key) + + if bin_data: + row.update(bin_data) + + args.allotted_qty = 0 + if bin_data and bin_data.get("actual_qty") > 0: + args.allotted_qty = (bin_data.get("actual_qty") + if (args.required_qty > bin_data.get("actual_qty")) else args.required_qty) + + args.remaining_qty -= args.allotted_qty + bin_data["actual_qty"] -= args.allotted_qty + + if ((self.mrp_warehouses and (args.allotted_qty or index == len(warehouses) - 1)) + or not self.mrp_warehouses): + if not self.index: + row.update(order_data) + self.index += 1 + + args.warehouse = warehouse + row.update(args) + if self.purchase_details.get(key): + row.update(self.purchase_details.get(key)) + + self.data.append(row) + + def get_args(self): + return frappe._dict({ + "work_order": "", + "sales_order": "", + "production_item": "", + "production_item_name": "", + "qty_to_manufacture": "", + "produced_qty": "" + }) + + def get_columns(self): + based_on = self.filters.based_on + + self.columns = [{ + "label": _("ID"), + "options": based_on, + "fieldname": "name", + "fieldtype": "Link", + "width": 100 + }, { + "label": _("Item Code"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, { + "label": _("Item Name"), + "fieldname": "production_item_name", + "fieldtype": "Data", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "for_warehouse", + "fieldtype": "Link", + "width": 100 + }, { + "label": _("Order Qty"), + "fieldname": "qty_to_manufacture", + "fieldtype": "Float", + "width": 80 + }, { + "label": _("Available"), + "fieldname": "available_qty", + "fieldtype": "Float", + "width": 80 + }] + + fieldname, fieldtype = "delivery_date", "Date" + if self.filters.based_on == "Sales Order" and self.filters.order_by == "Total Amount": + fieldname, fieldtype = "base_grand_total", "Currency" + elif self.filters.based_on == "Material Request": + fieldname = "schedule_date" + elif self.filters.based_on == "Work Order": + fieldname = "planned_start_date" + + self.columns.append({ + "label": _(self.filters.order_by), + "fieldname": fieldname, + "fieldtype": fieldtype, + "width": 100 + }) + + self.columns.extend([{ + "label": _("Raw Material Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, { + "label": _("Raw Material Name"), + "fieldname": "raw_material_name", + "fieldtype": "Data", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 110 + }, { + "label": _("Required Qty"), + "fieldname": "required_qty", + "fieldtype": "Float", + "width": 100 + }, { + "label": _("Allotted Qty"), + "fieldname": "allotted_qty", + "fieldtype": "Float", + "width": 100 + }, { + "label": _("Expected Arrival Date"), + "fieldname": "arrival_date", + "fieldtype": "Date", + "width": 160 + }, { + "label": _("Arrival Quantity"), + "fieldname": "arrival_qty", + "fieldtype": "Float", + "width": 140 + }]) + +def document_query(doctype, txt, searchfield, start, page_len, filters): + pass \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dc88ffb6bea..56186c4e4ca 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,3 +688,4 @@ erpnext.patches.v12_0.set_serial_no_status erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo +erpnext.patches.v12_0.update_bom_in_so_mr diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py new file mode 100644 index 00000000000..309ae4c2ab7 --- /dev/null +++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py @@ -0,0 +1,19 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("stock", "doctype", "material_request_item") + frappe.reload_doc("selling", "doctype", "sales_order_item") + + for doctype in ["Sales Order", "Material Request"]: + condition = " and child_doc.stock_qty > child_doc.produced_qty" + if doctype == "Material Request": + condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'" + + frappe.db.sql(""" UPDATE `tab{doc}` as doc, `tab{doc} Item` as child_doc, tabItem as item + SET + child_doc.bom_no = item.default_bom + WHERE + child_doc.item_code = item.name and child_doc.docstatus < 2 + and item.default_bom is not null and item.default_bom != '' {cond} + """.format(doc = doctype, cond = condition)) \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 45a43c5e7e9..705dcb8e03a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -34,6 +34,15 @@ frappe.ui.form.on("Sales Order", { } }; }) + + frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + return { + filters: { + "item": row.item_code + } + } + }); }, refresh: function(frm) { if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed' 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 73f233c537c..e59349926e6 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -72,6 +72,8 @@ "against_blanket_order", "blanket_order", "blanket_order_rate", + "manufacturing_section_section", + "bom_no", "planning_section", "projected_qty", "actual_qty", @@ -212,6 +214,7 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", + "print_hide": 0, "reqd": 1 }, { @@ -764,12 +767,24 @@ "fieldname": "against_blanket_order", "fieldtype": "Check", "label": "Against Blanket Order" - } + }, + { + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "manufacturing_section_section", + "fieldtype": "Section Break", + "label": "Manufacturing Section" + } ], "idx": 1, "istable": 1, - "links": [], - "modified": "2020-03-05 14:20:28.085117", + "modified": "2020-05-15 18:13:43.006493", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index db8bffda9dd..3562181e25f 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -30,7 +30,16 @@ frappe.ui.form.on('Material Request', { return { filters: {'company': doc.company} }; - }) + }); + + frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + return { + filters: { + "item": row.item_code + } + } + }); }, onload: function(frm) { 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 df140ffd754..32bd4a0a57a 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -53,6 +53,8 @@ "dimension_col_break", "cost_center", "section_break_37", + "bom_no", + "section_break_46", "page_break" ], "fields": [ @@ -371,8 +373,10 @@ "label": "Image" }, { + "depends_on": "eval:parent.material_request_type == \"Manufacture\"", "fieldname": "section_break_37", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Manufacturing" }, { "fieldname": "received_qty", @@ -428,12 +432,24 @@ "fieldtype": "Link", "label": "Source Warehouse (Material Transfer)", "options": "Warehouse" + }, + { + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "section_break_46", + "fieldtype": "Section Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-05-01 09:00:00.992835", + "modified": "2020-05-15 09:00:00.992835", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 11b64034198..0ed3b276e39 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -305,7 +305,8 @@ def get_basic_details(args, item, overwrite_warehouse=True): "weight_uom":item.weight_uom, "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0, "transaction_date": args.get("transaction_date"), - "against_blanket_order": args.get("against_blanket_order") + "against_blanket_order": args.get("against_blanket_order"), + "bom_no": item.get("default_bom") }) if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): From 9a914dc5925819f7d2257cc3d64e576f41da2f33 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 19:56:51 +0530 Subject: [PATCH 210/410] fix: Tax amount in GSTR-1 JSON --- erpnext/regional/report/gstr_1/gstr_1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index dd5bb4ae9b0..43b1ea83eb9 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,9 +60,6 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) for rate, items in items_based_on_rate.items(): - if inv in self.cgst_igst_invoices: - rate = rate/2 - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -120,9 +117,15 @@ class Gstr1Report(object): else: row.append(invoice_details.get(fieldname)) taxable_value = 0 + + if invoice in self.cgst_igst_invoices: + division_factor = 2 + else: + division_factor = 1 + for item_code, net_amount in self.invoice_items.get(invoice).items(): if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + if self.item_tax_rate.get(invoice) and tax_rate/division_factor in self.item_tax_rate.get(invoice, {}).get(item_code, []): taxable_value += abs(net_amount) elif not self.item_tax_rate.get(invoice): taxable_value += abs(net_amount) From 99d348ffa3d88cd5e0c8a21fcb66f4b62ae73fb6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 20:28:19 +0530 Subject: [PATCH 211/410] fix: Patch for Setting Missing Company in Healthcare DocTypes (#21790) * fix: patch for setting missing company in Healthcare DocTypes * fix: check doctype exists Co-authored-by: Nabin Hait --- erpnext/patches.txt | 1 + .../v13_0/set_company_field_in_healthcare_doctypes.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 56186c4e4ca..bb9bb97e64a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,4 +688,5 @@ erpnext.patches.v12_0.set_serial_no_status erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo +erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py new file mode 100644 index 00000000000..2c646b164fb --- /dev/null +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + company = frappe.db.get_single_value('Global Defaults', 'default_company') + doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] + for entry in doctypes: + if frappe.db.exists('DocType', entry): + frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 0c03834190c046e699c161e88e3d9c54abb1da73 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 20:30:20 +0530 Subject: [PATCH 212/410] fix(minor): pass ignore_mandatory flag for Lab Test Item Creation (#21784) --- .../healthcare/doctype/lab_test_template/lab_test_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index e2b47b45593..3521561f34a 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -115,7 +115,7 @@ def make_item_price(item, price_list_name, item_price): "price_list": price_list_name, "item_code": item, "price_list_rate": item_price - }).insert(ignore_permissions=True) + }).insert(ignore_permissions=True, ignore_mandatory=True) @frappe.whitelist() def change_test_code_from_template(lab_test_code, doc): From 1b1787c61a47430fc113e691ae2c6dd87f0082db Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 20:51:30 +0530 Subject: [PATCH 213/410] fix: Against voucher in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 6afe208b5b1..f83a2595f6a 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -296,7 +296,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - if data[key].against_voucher: + if data[key].against_voucher and gle.against_voucher: data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) From 041e31b8baa619c75e06226fed0a972ef773cdd5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 20:58:00 +0530 Subject: [PATCH 214/410] fix: added dashboard link in healthcare desk --- erpnext/healthcare/dashboard_fixtures.py | 26 +++++++++---------- .../desk_page/healthcare/healthcare.json | 8 +++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 4fb05a8f81f..59da71a0ec9 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -3,7 +3,7 @@ import frappe import json - +from frappe import _ def get_data(): return frappe._dict({ @@ -51,7 +51,7 @@ def get_charts(): "doctype": "Dashboard Chart", "time_interval": "Daily", "name": "Patient Appointments", - "chart_name": "Patient Appointments", + "chart_name": _("Patient Appointments"), "timespan": "Last Month", "filters_json": json.dumps([ ["Patient Appointment", "company", "=", company, False], @@ -68,7 +68,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Department wise Patient Appointments", - "chart_name": "Department wise Patient Appointments", + "chart_name": _("Department wise Patient Appointments"), "chart_type": "Custom", "source": "Department wise Patient Appointments", "filters_json": json.dumps({}), @@ -87,7 +87,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Lab Tests", - "chart_name": "Lab Tests", + "chart_name": _("Lab Tests"), "chart_type": "Group By", "document_type": "Lab Test", "group_by_type": "Count", @@ -104,7 +104,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Clinical Procedures", - "chart_name": "Clinical Procedures", + "chart_name": _("Clinical Procedures"), "chart_type": "Group By", "document_type": "Clinical Procedure", "group_by_type": "Count", @@ -121,7 +121,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "In-Patient Status", - "chart_name": "In-Patient Status", + "chart_name": _("In-Patient Status"), "chart_type": "Group By", "document_type": "Inpatient Record", "group_by_type": "Count", @@ -137,7 +137,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Clinical Procedures Status", - "chart_name": "Clinical Procedure Status", + "chart_name": _("Clinical Procedure Status"), "chart_type": "Group By", "document_type": "Clinical Procedure", "group_by_type": "Count", @@ -154,7 +154,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Symptoms", - "chart_name": "Symptoms", + "chart_name": _("Symptoms"), "chart_type": "Group By", "document_type": "Patient Encounter Symptom", "group_by_type": "Count", @@ -168,7 +168,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Diagnoses", - "chart_name": "Diagnoses", + "chart_name": _("Diagnoses"), "chart_type": "Group By", "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", @@ -186,7 +186,7 @@ def get_number_cards(): return [ { "name": "Total Patients", - "label": "Total Patients", + "label": _("Total Patients"), "function": "Count", "doctype": "Number Card", "document_type": "Patient", @@ -200,7 +200,7 @@ def get_number_cards(): }, { "name": "Total Patients Admitted", - "label": "Total Patients Admitted", + "label": _("Total Patients Admitted"), "function": "Count", "doctype": "Number Card", "document_type": "Patient", @@ -214,7 +214,7 @@ def get_number_cards(): }, { "name": "Open Appointments", - "label": "Open Appointments", + "label": _("Open Appointments"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", @@ -229,7 +229,7 @@ def get_number_cards(): }, { "name": "Appointments to Bill", - "label": "Appointments to Bill", + "label": _("Appointments to Bill"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 14ad5e450a9..60b53137cd7 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -60,10 +60,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 14:05:10.520457", + "modified": "2020-05-19 20:57:22.797267", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -106,6 +107,11 @@ "label": "Patient History", "link_to": "patient_history", "type": "Page" + }, + { + "label": "Healthcare Dashboard", + "link_to": "Healthcare", + "type": "Dashboard" } ] } \ No newline at end of file From 6277367d21f65c7e5c544178d6dac04ae1265701 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 21:18:26 +0530 Subject: [PATCH 215/410] fix: project desk --- erpnext/projects/dashboard_fixtures.py | 4 +++- erpnext/projects/desk_page/projects/projects.json | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/dashboard_fixtures.py b/erpnext/projects/dashboard_fixtures.py index 63b3893b76a..d89ffe9d83b 100644 --- a/erpnext/projects/dashboard_fixtures.py +++ b/erpnext/projects/dashboard_fixtures.py @@ -3,6 +3,7 @@ import frappe import json +from frappe import _ def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -37,10 +38,11 @@ def get_charts(): { 'doctype': 'Dashboard Chart', 'name': 'Project Summary', - 'chart_name': 'Project Summary', + 'chart_name': _('Project Summary'), 'chart_type': 'Report', 'report_name': 'Project Summary', 'is_public': 1, + 'is_custom': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 4d4450dbf53..fdbe13b2fb7 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -29,10 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-04 20:27:51.591365", + "modified": "2020-05-19 21:09:52.031828", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -65,6 +66,11 @@ "label": "Project Billing Summary", "link_to": "Project Billing Summary", "type": "Report" + }, + { + "label": "Project Dashboard", + "link_to": "Project", + "type": "Dashboard" } ] } \ No newline at end of file From 75e8b1b519c17d6f2ad46191674536699a034e46 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 19 May 2020 18:31:31 +0530 Subject: [PATCH 216/410] tax id is not fetched in when creating sales order from quoation --- erpnext/selling/doctype/sales_order/sales_order.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 6462d3bc8c3..b57c4f30981 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -282,6 +282,7 @@ "width": "100px" }, { + "fetch_from": "customer.tax_id", "fieldname": "tax_id", "fieldtype": "Data", "label": "Tax Id", @@ -1196,7 +1197,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:50:39.640534", + "modified": "2020-05-19 21:39:19.486684", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", From e6147ed9bc89639db843417704718ce98db5169c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 12 May 2020 11:18:32 +0530 Subject: [PATCH 217/410] feat: production forecasting using exponential smoothing method --- .../accounts/report/financial_statements.py | 7 +- .../__init__.py | 0 .../exponential_smoothing_forecasting.js | 97 ++++++++ .../exponential_smoothing_forecasting.json | 40 ++++ .../exponential_smoothing_forecasting.py | 222 ++++++++++++++++++ 5 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7fb598bd689..4a35a668658 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -19,7 +19,7 @@ from six import itervalues 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, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, - company=None, reset_period_on_fy_change=True): + company=None, reset_period_on_fy_change=True, ignore_fiscal_year=False): """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" @@ -67,8 +67,9 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ # if a fiscal year ends before a 12 month period period.to_date = year_end_date - period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] - period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] + if not ignore_fiscal_year: + period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] + period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] period_list.append(period) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js new file mode 100644 index 00000000000..123a82a3882 --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js @@ -0,0 +1,97 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Exponential Smoothing Forecasting"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), 12), + "reqd": 1 + }, + { + "fieldname":"based_on_document", + "label": __("Based On Document"), + "fieldtype": "Select", + "options": ["Sales Order", "Delivery Note", "Quotation"], + "default": "Sales Order", + "reqd": 1 + }, + { + "fieldname":"based_on_field", + "label": __("Based On"), + "fieldtype": "Select", + "options": ["Qty", "Amount"], + "default": "Qty", + "reqd": 1 + }, + { + "fieldname":"no_of_years", + "label": __("Based On Data ( in years )"), + "fieldtype": "Select", + "options": [3, 6, 9], + "default": 3, + "reqd": 1 + }, + { + "fieldname": "periodicity", + "label": __("Periodicity"), + "fieldtype": "Select", + "options": [ + { "value": "Monthly", "label": __("Monthly") }, + { "value": "Quarterly", "label": __("Quarterly") }, + { "value": "Half-Yearly", "label": __("Half-Yearly") }, + { "value": "Yearly", "label": __("Yearly") } + ], + "default": "Yearly", + "reqd": 1 + }, + { + "fieldname":"smoothing_constant", + "label": __("Smoothing Constant"), + "fieldtype": "Select", + "options": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], + "reqd": 1, + "default": 0.3 + }, + { + "fieldname":"item_code", + "label": __("Item Code"), + "fieldtype": "Link", + "options": "Item" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + if (company) { + return { + filters: { + 'company': company + } + }; + } + } + } + ] +}; diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json new file mode 100644 index 00000000000..5092ef4e7a5 --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json @@ -0,0 +1,40 @@ +{ + "add_total_row": 0, + "creation": "2020-05-15 05:18:55.838030", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-05-15 05:18:55.838030", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Exponential Smoothing Forecasting", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Exponential Smoothing Forecasting", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Sales Manager" + }, + { + "role": "Sales User" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py new file mode 100644 index 00000000000..b5127f133ca --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -0,0 +1,222 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from frappe.utils import flt, nowdate, add_years, cint, getdate +from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + +def execute(filters=None): + return ForecastingReport(filters).execute_report() + +class ExponentialSmoothingForecast(object): + def forecast_future_data(self): + for key, value in self.period_wise_data.items(): + forecast_data = [] + for period in self.period_list: + forecast_key = "forecast_" + period.key + + if value.get(period.key) and not forecast_data: + value[forecast_key] = flt(value.get("avg", 0)) or flt(value.get(period.key)) + + # will be use to forecaset next period + forecast_data.append([value.get(period.key), value.get(forecast_key)]) + elif forecast_data: + previous_period_data = forecast_data[-1] + value[forecast_key] = (previous_period_data[1] + + flt(self.filters.smoothing_constant) * ( + flt(previous_period_data[0]) - flt(previous_period_data[1]) + ) + ) + +class ForecastingReport(ExponentialSmoothingForecast): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.data = [] + self.doctype = self.filters.based_on_document + self.child_doctype = self.doctype + " Item" + self.based_on_field = ("qty" + if self.filters.based_on_field == "Qty" else "amount") + self.fieldtype = "Float" if self.based_on_field == "qty" else "Currency" + self.company_currency = erpnext.get_company_currency(self.filters.company) + + def execute_report(self): + self.prepare_periodical_data() + self.forecast_future_data() + self.data = self.period_wise_data.values() + self.add_total() + + columns = self.get_columns() + charts = self.get_chart_data() + summary_data = self.get_summary_data() + + return columns, self.data, None, charts, summary_data + + def prepare_periodical_data(self): + self.period_wise_data = {} + + from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1) + self.period_list = get_period_list(from_date, self.filters.to_date, + from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True) + + order_data = self.get_data_for_forecast() or [] + + for entry in order_data: + key = (entry.item_code, entry.warehouse) + if key not in self.period_wise_data: + self.period_wise_data[key] = entry + + period_data = self.period_wise_data[key] + for period in self.period_list: + # check if posting date is within the period + if (entry.posting_date >= period.from_date and entry.posting_date <= period.to_date): + period_data[period.key] = period_data.get(period.key, 0.0) + flt(entry.get(self.based_on_field)) + + for key, value in self.period_wise_data.items(): + list_of_period_value = [value.get(p.key, 0) for p in self.period_list] + + if list_of_period_value: + value["avg"] = sum(list_of_period_value) / len(list_of_period_value) + + def get_data_for_forecast(self): + cond = "" + if self.filters.item_code: + cond = " AND soi.item_code = %s" %(frappe.db.escape(self.filters.item_code)) + + warehouses = [] + if self.filters.warehouse: + warehouses = get_child_warehouses(self.filters.warehouse) + cond += " AND soi.warehouse in ({})".format(','.join(['%s'] * len(warehouses))) + + input_data = [self.filters.from_date, self.filters.company] + if warehouses: + input_data.extend(warehouses) + + date_field = "posting_date" if self.doctype == "Delivery Note" else "transaction_date" + + return frappe.db.sql(""" + SELECT + so.{date_field} as posting_date, soi.item_code, soi.warehouse, + soi.item_name, soi.stock_qty as qty, soi.base_amount as amount + FROM + `tab{doc}` so, `tab{child_doc}` soi + WHERE + so.docstatus = 1 AND so.name = soi.parent AND + so.{date_field} < %s AND so.company = %s {cond} + """.format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond), + tuple(input_data), as_dict=1) + + def add_total(self): + total_row = { + "item_code": _(frappe.bold("Total Quantity")) + } + + for value in self.data: + for period in self.period_list: + forecast_key = "forecast_" + period.key + if forecast_key not in total_row: + total_row.setdefault(forecast_key, 0.0) + + if period.key not in total_row: + total_row.setdefault(period.key, 0.0) + + total_row[forecast_key] += value.get(forecast_key, 0.0) + total_row[period.key] += value.get(period.key, 0.0) + + self.data.append(total_row) + + def get_columns(self): + columns = [{ + "label": _("Item Code"), + "options": "Item", + "fieldname": "item_code", + "fieldtype": "Link", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 130 + }] + + width = 150 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 + for period in self.period_list: + if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] + or period.from_date >= getdate(self.filters.from_date)): + + forecast_key = 'forecast_' + period.key + + columns.append({ + "label": _(period.label), + "fieldname": forecast_key, + "fieldtype": self.fieldtype, + "width": width, + "default": 0.0 + }) + + return columns + + def get_chart_data(self): + if not self.data: return + + labels = [] + self.total_demand = [] + self.total_forecast = [] + self.total_history_forecast = [] + self.total_future_forecast = [] + + for period in self.period_list: + forecast_key = "forecast_" + period.key + + labels.append(_(period.label)) + + if period.from_date < getdate(self.filters.from_date): + self.total_demand.append(self.data[-1].get(period.key, 0)) + self.total_history_forecast.append(self.data[-1].get(forecast_key, 0)) + else: + self.total_future_forecast.append(self.data[-1].get(forecast_key, 0)) + + self.total_forecast.append(self.data[-1].get(forecast_key, 0)) + + return { + "data": { + "labels": labels, + "datasets": [ + { + "name": "Demand", + "values": self.total_demand + }, + { + "name": "Forecast", + "values": self.total_forecast + } + ] + }, + "type": "line" + } + + def get_summary_data(self): + return [ + { + "value": sum(self.total_demand), + "label": _("Total Demand (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_history_forecast), + "label": _("Total Forecast (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_future_forecast), + "indicator": "Green", + "label": _("Total Forecast (Future Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + } + ] \ No newline at end of file From 841a8db5d1d28e3618924f8548ee7e100f485c60 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 20 May 2020 12:06:03 +0530 Subject: [PATCH 218/410] updated manufacturing desk --- .../manufacturing/manufacturing.json | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index ecd2dc9b769..f2e07bfe204 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -43,10 +43,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 14:05:59.100891", + "modified": "2020-05-20 11:50:20.029056", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -88,6 +89,17 @@ "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, + { + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + }, + { + "label": "Forecasting", + "link_to": "Exponential Smoothing Forecasting", + "type": "Report" + }, { "label": "Work Order Summary", "link_to": "Work Order Summary", @@ -95,10 +107,14 @@ "type": "Report" }, { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "type": "Report" + }, + { + "label": "Production Planning Report", + "link_to": "Production Planning Report", + "type": "Report" } ] } \ No newline at end of file From 7cbbd68176680aa62ba1249fea799337d2ddbf81 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 12:55:34 +0530 Subject: [PATCH 219/410] fix: CRM module dashboard and onboarding --- erpnext/crm/dashboard_fixtures.py | 135 +++++++++--------- erpnext/crm/desk_page/crm/crm.json | 8 +- erpnext/crm/module_onboarding/crm/crm.json | 11 +- .../create_and_send_quotation.json | 4 +- .../create_lead/create_lead.json | 4 +- .../create_opportunity.json | 4 +- .../introduction_to_crm.json | 2 + .../start_campaign/start_campaign.json | 17 --- 8 files changed, 89 insertions(+), 96 deletions(-) delete mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 5b1ac9bf5e2..16904b34295 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe, erpnext, json +from frappe import _ def get_data(): return frappe._dict({ @@ -16,19 +17,19 @@ def get_dashboards(): "name": "CRM", "dashboard_name": "CRM", "charts": [ - { "chart": "Lead", "width": "Full" }, - { "chart": "Opportunity", "width": "Full"}, - { "chart": "Campaign", "width": "Half" }, - { "chart": "Opportunities Won", "width": "Half" }, - { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Incoming Leads", "width": "Full" }, + { "chart": "Opportunity Trends", "width": "Full"}, + { "chart": "Won Opportunities", "width": "Full" }, + { "chart": "Territory Wise Opportunity Count", "width": "Half"}, { "chart": "Territory Wise Sales", "width": "Half"}, - { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Opportunities via Campaigns", "width": "Half" }, { "chart": "Lead Source", "width": "Half"} ], "cards": [ - { "card": "New Lead" }, - { "card": "New Opportunity" }, - { "card": "Won Opportunity" }, + { "card": "New Lead (Last 1 Month)" }, + { "card": "New Opportunity (Last 1 Month)" }, + { "card": "Won Opportunity (Last 1 Month)" }, + { "card": "Open Opportunity"}, ] }] @@ -46,11 +47,11 @@ def get_charts(): company = get_company_for_dashboards() return [{ - "name": "Lead", + "name": "Incoming Leads", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Lead", + "chart_name": _("Incoming Leads"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Lead", @@ -62,11 +63,11 @@ def get_charts(): "type": "Bar" }, { - "name": "Opportunity", + "name": "Opportunity Trends", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunity", + "chart_name": _("Opportunity Trends"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Opportunity", @@ -78,28 +79,25 @@ def get_charts(): "type": "Bar" }, { - "name": "Campaign", + "name": "Opportunities via Campaigns", + "chart_name": _("Opportunities via Campaigns"), "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Campaign", - "timespan": "Last Year", - "time_interval": "Monthly", - "document_type": "Campaign", - "based_on": "creation", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "campaign", + "document_type": "Opportunity", 'is_public': 1, 'timeseries': 1, "owner": "Administrator", "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), - "type": "Line", - "color": "#428b46" + "type": "Pie" }, { - "name": "Opportunities Won", + "name": "Won Opportunities", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunities Won", + "chart_name": _("Won Opportunities"), "timespan": "Last Year", "time_interval": "Monthly", "document_type": "Opportunity", @@ -107,20 +105,25 @@ def get_charts(): 'is_public': 1, 'timeseries': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False]]), "type": "Bar" }, { - "name": "Territory Wise Opportunity", + "name": "Territory Wise Opportunity Count", "doctype": "Dashboard Chart", "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "territory", - "chart_name": "Territory Wise Opportunity", + "chart_name": _("Territory Wise Opportunity Count"), "document_type": "Opportunity", 'is_public': 1, + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False] + ]), "owner": "Administrator", - "type": "Line" + "type": "Donut" }, { "name": "Territory Wise Sales", @@ -128,31 +131,16 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Sum", "group_by_based_on": "territory", - "chart_name": "Territory Wise Sales", + "chart_name": _("Territory Wise Sales"), "aggregate_function_based_on": "opportunity_amount", "document_type": "Opportunity", 'is_public': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), - "type": "Bar", - "color": "#7575ff" - }, - { - "name": "Qualified For Call", - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Qualified For Call", - "timespan": "Last Quarter", - "time_interval": "Weekly", - "document_type": "Opportunity", - "based_on": "modified", - 'is_public': 1, - 'timeseries': 1, - "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), - "type": "Line", - "color": "#fff168" + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False] + ]), + "type": "Donut" }, { "name": "Lead Source", @@ -160,44 +148,55 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "source", - "chart_name": "Lead Source", + "chart_name": _("Lead Source"), "document_type": "Lead", 'is_public': 1, "owner": "Administrator", - "type": "Donut" + "type": "Pie" }] def get_number_cards(): return [{ "doctype": "Number Card", "document_type": "Lead", - "name": "New Lead", - "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "name": "New Lead (Last 1 Month)", + "filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]), "function": "Count", "is_public": 1, - "label": "New Lead", + "label": _("New Lead (Last 1 Month)"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }, { "doctype": "Number Card", "document_type": "Opportunity", - "name": "New Opportunity", + "name": "New Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("New Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("Won Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Open Opportunity", "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), "function": "Count", "is_public": 1, - "label": "New Opportunity", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "doctype": "Number Card", - "document_type": "Opportunity", - "name": "Won Opportunity", - "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), - "function": "Count", - "is_public": 1, - "label": "Won Opportunity", + "label": _("Open Opportunity"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 80d3d18de97..2fc4582917a 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -38,10 +38,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-14 17:20:07.636751", + "modified": "2020-05-20 12:11:36.250491", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -73,6 +74,11 @@ "label": "Sales Analytics", "link_to": "Sales Analytics", "type": "Report" + }, + { + "label": "CRM Dashboard", + "link_to": "CRM", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 9129c2625a0..694763f7c7f 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -4,10 +4,10 @@ "role": "Sales Master Manager" }, { - "role": "Administrator" + "role": "Sales Manager" }, { - "role": "Sales Manager" + "role": "Sales User" } ], "creation": "2020-05-09 23:42:50.901548", @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 17:41:11.083917", + "modified": "2020-05-20 12:53:47.029412", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -25,9 +25,6 @@ { "step": "Introduction to CRM" }, - { - "step": "Start Campaign" - }, { "step": "Create Lead" }, @@ -38,7 +35,7 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "subtitle": "Lead, Opportunity, Customer and more", "success_message": "CRM Module is all setup!", "title": "Let's Setup Your CRM", "user_can_dismiss": 1 diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 0997017933a..a6edfd7e53d 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -13,5 +13,7 @@ "name": "Create and Send Quotation", "owner": "Administrator", "reference_document": "Quotation", - "title": "Create and Send Quotation" + "show_full_form": 0, + "title": "Create and Send Quotation", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index b1076e0bf95..47a45d70a8c 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -13,5 +13,7 @@ "name": "Create Lead", "owner": "Administrator", "reference_document": "Lead", - "title": "Create Lead" + "show_full_form": 0, + "title": "Create Lead", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json index 20949a435bc..231cf17bb57 100644 --- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -13,5 +13,7 @@ "name": "Create Opportunity", "owner": "Administrator", "reference_document": "Opportunity", - "title": "Create Opportunity" + "show_full_form": 0, + "title": "Create Opportunity", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index c923a77e1b9..552ade0fdda 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -12,6 +12,8 @@ "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to CRM", + "validate_action": 0, "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json deleted file mode 100644 index 53c3becea3b..00000000000 --- a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-09 23:38:10.284957", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 17:28:54.285958", - "modified_by": "Administrator", - "name": "Start Campaign", - "owner": "Administrator", - "reference_document": "Campaign", - "title": "Start Campaign" -} \ No newline at end of file From ca2b3231177bd10e9b41d8155239bee06e71ef62 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 20 May 2020 14:56:29 +0530 Subject: [PATCH 220/410] fix: Move address and contact templates to frappe --- erpnext/public/build.json | 2 - erpnext/public/js/templates/address_list.html | 22 -------- erpnext/public/js/templates/contact_list.html | 54 ------------------- 3 files changed, 78 deletions(-) delete mode 100644 erpnext/public/js/templates/address_list.html delete mode 100644 erpnext/public/js/templates/contact_list.html diff --git a/erpnext/public/build.json b/erpnext/public/build.json index e94d1ffe5c0..2695502269a 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -23,8 +23,6 @@ "public/js/queries.js", "public/js/sms_manager.js", "public/js/utils/party.js", - "public/js/templates/address_list.html", - "public/js/templates/contact_list.html", "public/js/controllers/stock_controller.js", "public/js/payment/payments.js", "public/js/controllers/taxes_and_totals.js", diff --git a/erpnext/public/js/templates/address_list.html b/erpnext/public/js/templates/address_list.html deleted file mode 100644 index 0f967b67a0f..00000000000 --- a/erpnext/public/js/templates/address_list.html +++ /dev/null @@ -1,22 +0,0 @@ -
    -{% for(var i=0, l=addr_list.length; i -

    - {%= i+1 %}. {%= addr_list[i].address_title %}{% if(addr_list[i].address_type!="Other") { %} - ({%= __(addr_list[i].address_type) %}){% } %} - {% if(addr_list[i].is_primary_address) { %} - ({%= __("Primary") %}){% } %} - {% if(addr_list[i].is_shipping_address) { %} - ({%= __("Shipping") %}){% } %} - - - {%= __("Edit") %} -

    -

    {%= addr_list[i].display %}

    - -{% } %} -{% if(!addr_list.length) { %} -

    {%= __("No address added yet.") %}

    -{% } %} -

    \ No newline at end of file diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html deleted file mode 100644 index 7e6969163b7..00000000000 --- a/erpnext/public/js/templates/contact_list.html +++ /dev/null @@ -1,54 +0,0 @@ -
    -{% for(var i=0, l=contact_list.length; i -

    - {%= contact_list[i].first_name %} {%= contact_list[i].last_name %} - {% if(contact_list[i].is_primary_contact) { %} - ({%= __("Primary") %}) - {% } %} - {% if(contact_list[i].designation){ %} - – {%= contact_list[i].designation %} - {% } %} - - {%= __("Edit") %} -

    - {% if (contact_list[i].phones || contact_list[i].email_ids) { %} -

    - {% if(contact_list[i].phone) { %} - {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
    - {% endif %} - {% if(contact_list[i].mobile_no) { %} - {%= __("Mobile No") %}: {%= contact_list[i].mobile_no %} ({%= __("Primary") %})
    - {% endif %} - {% if(contact_list[i].phone_nos) { %} - {% for(var j=0, k=contact_list[i].phone_nos.length; j - {% } %} - {% endif %} -

    -

    - {% if(contact_list[i].email_id) { %} - {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
    - {% endif %} - {% if(contact_list[i].email_ids) { %} - {% for(var j=0, k=contact_list[i].email_ids.length; j - {% } %} - {% endif %} -

    - {% endif %} -

    - {% if (contact_list[i].address) { %} - {%= __("Address") %}: {%= contact_list[i].address %}
    - {% endif %} -

    - -{% } %} -{% if(!contact_list.length) { %} -

    {%= __("No contacts added yet.") %}

    -{% } %} -

    -

    \ No newline at end of file From 91a9d3fbe56ddee81babd3c64c0760749804bfcb Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:12:46 +0530 Subject: [PATCH 221/410] fix: Reload doc in Healthcare company patch (#21808) --- .../patches/v13_0/set_company_field_in_healthcare_doctypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index 2c646b164fb..9d0dae45535 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -6,4 +6,5 @@ def execute(): doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] for entry in doctypes: if frappe.db.exists('DocType', entry): + frappe.reload_doc("Healthcare", "doctype", entry) frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 2b04e8eef096f52d93473f4537131c2233fb441e Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:43 +0530 Subject: [PATCH 222/410] fix: Validate Payment Gateway only if it exists in Payment Request. (#21805) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 68aeb6d1d65..287e00f70fd 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From 2346353a2231082868cd07de23a0ab12ea310c5e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 20 May 2020 16:14:09 +0530 Subject: [PATCH 223/410] fix: missing parameter (#21802) --- .../monthly_attendance_sheet/monthly_attendance_sheet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 efc43bf1d1b..60767b5db08 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -66,11 +66,11 @@ def execute(filters=None): emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) emp_att_map.update(aaa) data += record else: - record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) data += record chart_data = get_chart_data(emp_att_map, days) @@ -120,7 +120,7 @@ def get_chart_data(emp_att_map, days): return chart -def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): +def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=None): record = [] emp_att_map = {} From ab250e4ed55aac4f3f451108b548f3fb71a31e09 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:15:48 +0530 Subject: [PATCH 224/410] enable Allow Rename in sales stage (#21799) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb58314..77aa559b771 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stage Name", - "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, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 12:22:01.866472", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 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", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From e091789332662a4e27d4c99cc788cb2cf321e7b4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 20 May 2020 16:17:12 +0530 Subject: [PATCH 225/410] feat: remove website settings from boot (#21801) --- erpnext/startup/boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 4ca43a89b8f..2b80fb8dfa1 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -10,7 +10,6 @@ def boot_session(bootinfo): """boot session - send website info if guest""" bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or '' - bootinfo.website_settings = frappe.get_doc('Website Settings') if frappe.session['user']!='Guest': update_page_info(bootinfo) From 22aec57bebb395b1f309ead0211d1819d197c622 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:39:17 +0530 Subject: [PATCH 226/410] feat: asset onboarding, dashboards --- erpnext/assets/dashboard_fixtures.py | 96 ++++++++ erpnext/assets/desk_page/assets/assets.json | 4 +- erpnext/assets/doctype/asset/asset.js | 3 +- erpnext/assets/doctype/asset/asset.py | 2 + erpnext/assets/onboarding/assets/assets.json | 39 ++++ .../create_a_fixed_asset_item.json | 16 ++ .../create_the_asset/create_the_asset.json | 16 ++ .../introduction_to_assets.json | 16 ++ .../purchase_the_asset_item.json | 16 ++ .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ++++ .../category_wise_asset_value.json | 29 +++ .../category_wise_asset_value.py | 137 ++++++++++++ .../fixed_asset_register.js | 18 +- .../fixed_asset_register.py | 211 ++++++++++-------- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ++++ .../location_wise_asset_value.json | 29 +++ .../location_wise_asset_value.py | 137 ++++++++++++ 19 files changed, 752 insertions(+), 103 deletions(-) create mode 100644 erpnext/assets/dashboard_fixtures.py create mode 100644 erpnext/assets/onboarding/assets/assets.json create mode 100644 erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json create mode 100644 erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json create mode 100644 erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json create mode 100644 erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json create mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py create mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py new file mode 100644 index 00000000000..aa4eed2f746 --- /dev/null +++ b/erpnext/assets/dashboard_fixtures.py @@ -0,0 +1,96 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "Asset", + "dashboard_name": "Asset", + "charts": [ + { "chart": "Category-wise Asset Value" }, + { "chart": "Location-wise Asset Value" }, + ] + }] + +def get_charts(): + return [ + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Category-wise Asset Value", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] + + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value ", + "function": "Sum", + "aggregate_function_based_on": "gross_purchase_amount", + "document_type": "Asset", + "is_public": 0, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card" + } + ] \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 03094160e5f..13709e370bb 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -24,14 +24,14 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-04-01 11:28:51.072198", + "modified": "2020-05-08 16:07:04.671296", "modified_by": "Administrator", "module": "Assets", "name": "Assets", + "onboarding": "Assets", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index a53ff881777..fba20c0c879 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -387,7 +387,8 @@ frappe.ui.form.on('Asset', { } frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); - frm.set_value('location', item.asset_location); + item.asset_location && frm.set_value('location', item.asset_location); + frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); }, set_depreciation_rate: function(frm, row) { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 505ba4c6b64..2ecabe60f03 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -127,6 +127,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/onboarding/assets/assets.json new file mode 100644 index 00000000000..b32a7f2be5b --- /dev/null +++ b/erpnext/assets/onboarding/assets/assets.json @@ -0,0 +1,39 @@ +{ + "allow_roles": [ + { + "role": "Accounts User" + }, + { + "role": "Maintenance User" + } + ], + "creation": "2020-05-08 15:10:45.571457", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-08 16:17:31.685943", + "modified_by": "Administrator", + "module": "Assets", + "name": "Assets", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Assets" + }, + { + "step": "Create a Fixed Asset Item" + }, + { + "step": "Purchase the Asset Item" + }, + { + "step": "Create the Asset" + } + ], + "subtitle": "Assets, Depreciations, Repairs and more", + "success_message": "The Asset Module is all set up!", + "title": "Let's Setup Asset Management", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json new file mode 100644 index 00000000000..f5818c091f1 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:20:00.259985", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:20:00.259985", + "modified_by": "Administrator", + "name": "Create a Fixed Asset Item", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Fixed Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json new file mode 100644 index 00000000000..28d8485f976 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create the Asset", + "owner": "Administrator", + "reference_document": "Asset", + "title": "Create the Asset" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json new file mode 100644 index 00000000000..8aceafd7e5f --- /dev/null +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-08 13:18:25.424715", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 16:06:16.625646", + "modified_by": "Administrator", + "name": "Introduction to Assets", + "owner": "Administrator", + "title": "Introduction to Fixed Asset Management", + "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json new file mode 100644 index 00000000000..b6f38decb9e --- /dev/null +++ b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:28.208059", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:28.208059", + "modified_by": "Administrator", + "name": "Purchase the Asset Item", + "owner": "Administrator", + "reference_document": "Purchase Receipt", + "title": "Purchase the Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js new file mode 100644 index 00000000000..aa643efb2df --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Category-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json new file mode 100644 index 00000000000..a6dbce05163 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:36:02.116096", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:36:02.116096", + "modified_by": "Administrator", + "module": "Assets", + "name": "Category-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Category-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py new file mode 100644 index 00000000000..96131e92605 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "asset_category", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="asset_category") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "asset_category": asset.asset_category, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 91ce9ce7fe9..d68993c2976 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -30,18 +30,24 @@ frappe.query_reports["Fixed Asset Register"] = { label: __("Available For Use Date"), fieldtype: "Date" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"asset_category", label: __("Asset Category"), fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index fa2fe7b4a3c..64ee6a35037 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,105 +10,13 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - return columns, data + chart = prepare_chart_data(data, columns) -def get_columns(filters): - return [ - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 100 - }, - { - "label": _("Status"), - "fieldtype": "Data", - "fieldname": "status", - "width": 90 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "options": "Currency", - "width": 90 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "options": "Currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Cost Center"), - "fieldtype": "Link", - "fieldname": "cost_center", - "options": "Cost Center", - "width": 100 - }, - { - "label": _("Department"), - "fieldtype": "Link", - "fieldname": "department", - "options": "Department", - "width": 100 - }, - { - "label": _("Vendor Name"), - "fieldtype": "Data", - "fieldname": "vendor_name", - "width": 100 - }, - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 100 - }, - ] + return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status - date = filters.date if filters.get('company'): conditions["company"] = filters.company @@ -120,6 +28,8 @@ def get_conditions(filters): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): conditions["asset_category"] = filters.get('asset_category') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') # In Store assets are those that are not sold or scrapped operand = 'not in' @@ -169,6 +79,22 @@ def get_data(filters): return data +def prepare_chart_data(data, columns): + label_values_map = {} + for d in data: + if not label_values_map.get(d.get('asset_category')): + label_values_map[d.get('asset_category')] = 0 + label_values_map[d.get('asset_category')] += d.get('asset_value') + + return { + "data" : { + "labels": label_values_map.keys(), + "datasets": [{ "values": label_values_map.values() }] + }, + "type": 'donut', + "height": 250 + } + def get_finance_book_value_map(filters): date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() @@ -201,3 +127,100 @@ def get_purchase_invoice_supplier_map(): AND pii.is_fixed_asset=1 AND pi.docstatus=1 AND pi.is_return=0''')) + +def get_columns(filters): + return [ + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 60 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 100 + }, + { + "label": _("Status"), + "fieldtype": "Data", + "fieldname": "status", + "width": 80 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Cost Center"), + "fieldtype": "Link", + "fieldname": "cost_center", + "options": "Cost Center", + "width": 100 + }, + { + "label": _("Department"), + "fieldtype": "Link", + "fieldname": "department", + "options": "Department", + "width": 100 + }, + { + "label": _("Vendor Name"), + "fieldtype": "Data", + "fieldname": "vendor_name", + "width": 100 + }, + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 100 + }, + ] \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js new file mode 100644 index 00000000000..efdafa18c5c --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Location-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json new file mode 100644 index 00000000000..2a554d83d0e --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:47:55.036143", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:47:55.036143", + "modified_by": "Administrator", + "module": "Assets", + "name": "Location-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Location-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py new file mode 100644 index 00000000000..6aade0e27aa --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "location", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="location") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "location": asset.location, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file From 03007de4d79273df5072731eb5ee4139d2d25b88 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:55:19 +0530 Subject: [PATCH 227/410] fix: unexpected removal of finance book filter --- .../report/fixed_asset_register/fixed_asset_register.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index d68993c2976..a88f6accb89 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -36,6 +36,12 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"cost_center", label: __("Cost Center"), From 1dcf10339879e58dbbc4354eaa7ab8582e2c48c3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 14:55:38 +0530 Subject: [PATCH 228/410] chore: enhance fixed asset report and remove redundant reports --- .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ------ .../category_wise_asset_value.json | 29 ---- .../category_wise_asset_value.py | 137 ------------------ .../fixed_asset_register.js | 7 + .../fixed_asset_register.py | 61 +++++++- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ------ .../location_wise_asset_value.json | 29 ---- .../location_wise_asset_value.py | 137 ------------------ 10 files changed, 62 insertions(+), 424 deletions(-) delete mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js deleted file mode 100644 index aa643efb2df..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Category-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json deleted file mode 100644 index a6dbce05163..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:36:02.116096", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:36:02.116096", - "modified_by": "Administrator", - "module": "Assets", - "name": "Category-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Category-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py deleted file mode 100644 index 96131e92605..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "asset_category", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="asset_category") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "asset_category": asset.asset_category, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a88f6accb89..e886a35e5d3 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -54,6 +54,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Finance Book" }, + { + fieldname:"group_by", + label: __("Group By"), + fieldtype: "Select", + options: " \nAsset Category\nLocation", + default: '', + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 64ee6a35037..c6b0c4ecf53 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -44,23 +44,33 @@ def get_data(filters): data = [] - conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - assets_record = frappe.db.get_all("Asset", - filters=conditions, - fields=["name", "asset_name", "department", "cost_center", "purchase_receipt", + conditions = get_conditions(filters) + group_by = frappe.scrub(filters.get("group_by") or "") + + if group_by: + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] + else: + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + print(assets_record) + else: + fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", - "available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"]) + "available_for_use_date", "purchase_invoice", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - flt(depreciation_amount_map.get(asset.name)) if asset_value: row = { - "asset_id": asset.name, + "asset_id": asset.asset_id, "asset_name": asset.asset_name, "status": asset.status, "department": asset.department, @@ -129,6 +139,45 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): + if filters.get("group_by"): + return [ + { + "label": _("{}").format(filters.get("group_by")), + "fieldtype": "Link", + "fieldname": frappe.scrub(filters.get("group_by")), + "options": filters.get("group_by"), + "width": 120 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] + return [ { "label": _("Asset Id"), diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js deleted file mode 100644 index efdafa18c5c..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Location-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json deleted file mode 100644 index 2a554d83d0e..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:47:55.036143", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:47:55.036143", - "modified_by": "Administrator", - "module": "Assets", - "name": "Location-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Location-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py deleted file mode 100644 index 6aade0e27aa..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "location", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="location") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "location": asset.location, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file From ecddf3358d69c56a438d404886d9458e86a1f51f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:12:11 +0530 Subject: [PATCH 229/410] fix: dashboard fixtures --- erpnext/assets/dashboard_fixtures.py | 125 ++++++++---------- .../fixed_asset_register.py | 25 ++-- 2 files changed, 68 insertions(+), 82 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index aa4eed2f746..22c4a3e4f9e 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -8,8 +8,7 @@ import json def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts() }) def get_dashboards(): @@ -24,73 +23,55 @@ def get_dashboards(): def get_charts(): return [ - { - "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", - "chart_type": "Report", - "report_name": "Category-wise Asset Value", - "is_custom": 1, - "x_field": "asset_category", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Category-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Location-wise Asset Value", - "chart_name": "Location-wise Asset Value", - "chart_type": "Report", - "report_name": "Location-wise Asset Value", - "is_custom": 1, - "x_field": "location", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Location-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - } - ] - - -def get_number_cards(): - return [ - { - "name": "Asset Value", - "label": "Asset Value ", - "function": "Sum", - "aggregate_function_based_on": "gross_purchase_amount", - "document_type": "Asset", - "is_public": 0, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "filters_json": "[]", - "doctype": "Number Card" - } - ] \ No newline at end of file + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Fixed Asset Report", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }] diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index c6b0c4ecf53..81db57636b2 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,7 +10,7 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data, columns) + chart = prepare_chart_data(data) if not filters.get("group_by") else {} return columns, data, None, chart @@ -89,20 +89,25 @@ def get_data(filters): return data -def prepare_chart_data(data, columns): - label_values_map = {} +def prepare_chart_data(data): + labels, asset_values, depreciated_amounts = [], [], [] for d in data: - if not label_values_map.get(d.get('asset_category')): - label_values_map[d.get('asset_category')] = 0 - label_values_map[d.get('asset_category')] += d.get('asset_value') + labels.append(d.asset_id) + asset_values.append(d.asset_value) + depreciated_amounts.append(d.depreciated_amount) return { "data" : { - "labels": label_values_map.keys(), - "datasets": [{ "values": label_values_map.values() }] + "labels": labels, + "datasets": [ + { 'name': _('Asset Value'), 'values': asset_values }, + { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 }, - "type": 'donut', - "height": 250 } def get_finance_book_value_map(filters): From 3882939fd9daf057f93871c148cd89e340503aa2 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:49:18 +0530 Subject: [PATCH 230/410] fix: dashboard fixtures and FAR chart --- erpnext/assets/dashboard_fixtures.py | 2 +- .../introduction_to_assets/introduction_to_assets.json | 2 +- .../report/fixed_asset_register/fixed_asset_register.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 22c4a3e4f9e..93a06544f0b 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -53,7 +53,7 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Location-wise Asset Value", + "report_name": "Fixed Asset Report", "is_custom": 1, "x_field": "location", "timeseries": 0, diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json index 8aceafd7e5f..d48dd1cd3d2 100644 --- a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -11,6 +11,6 @@ "modified_by": "Administrator", "name": "Introduction to Assets", "owner": "Administrator", - "title": "Introduction to Fixed Asset Management", + "title": "Introduction to Assets", "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" } \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 81db57636b2..0b4b3343264 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -92,9 +92,9 @@ def get_data(filters): def prepare_chart_data(data): labels, asset_values, depreciated_amounts = [], [], [] for d in data: - labels.append(d.asset_id) - asset_values.append(d.asset_value) - depreciated_amounts.append(d.depreciated_amount) + labels.append(d.get("asset_id")) + asset_values.append(d.get("asset_value")) + depreciated_amounts.append(d.get("depreciated_amount")) return { "data" : { From 8990697fee02a5680ecfe2de4ffe5dd6f6904e36 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 May 2020 17:38:34 +0530 Subject: [PATCH 231/410] feat: fixed asset register based on fiscal year *asset value chart --- erpnext/assets/dashboard_fixtures.py | 105 ++++++++++++++++-- erpnext/assets/desk_page/assets/assets.json | 13 ++- .../fixed_asset_register.js | 70 +++++++++--- .../fixed_asset_register.py | 62 +++++++---- 4 files changed, 199 insertions(+), 51 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 93a06544f0b..84e67cf648e 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,12 +3,13 @@ import frappe import json - +from frappe.utils import nowdate, add_months def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts() + "charts": get_charts(), + "number_cards": get_number_cards(), }) def get_dashboards(): @@ -16,22 +17,63 @@ def get_dashboards(): "name": "Asset", "dashboard_name": "Asset", "charts": [ - { "chart": "Category-wise Asset Value" }, - { "chart": "Location-wise Asset Value" }, + { "chart": "Asset Value Analytics", "width": "Full" }, + { "chart": "Category-wise Asset Value", "width": "Half" }, + { "chart": "Location-wise Asset Value", "width": "Half" }, ] }] def get_charts(): + company = get_company_for_dashboards() + fiscal_year = get_fiscal_year() + return [ + { + "name": "Asset Value Analytics", + "chart_name": "Asset Value Analytics", + "chart_type": "Report", + "report_name": "Fixed Asset Register", + "is_custom": 1, + "group_by_type": "Count", + "number_of_groups": 0, + "is_public": 0, + "timespan": "Last Year", + "time_interval": "Yearly", + "timeseries": 0, + "filters_json": json.dumps({ + "company": company, + "status": "In Location", + "filter_based_on": "Fiscal Year", + "from_fiscal_year": fiscal_year, + "to_fiscal_year": fiscal_year, + "period_start_date": add_months(nowdate(), -12), + "period_end_date": nowdate(), + "date_based_on": "Purchase Date", + "group_by": "--Select a group--" + }), + "type": "Bar", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": { "stacked": 1 }, + "axisOptions": { "shortenYAxisNumbers": 1 }, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "y_axis": [] + }, { "name": "Category-wise Asset Value", "chart_name": "Category-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "asset_category", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Asset Category", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -53,11 +95,15 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "location", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Location", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -74,4 +120,41 @@ def get_charts(): "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} }) - }] + } + ] + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value", + "function": "Sum", + "aggregate_function_based_on": "value_after_depreciation", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + } + ] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_fiscal_year(): + fiscal_year = frappe.defaults.get_defaults().fiscal_year + if fiscal_year: + return fiscal_year + else: + fiscal_year_list = frappe.get_list("Fiscal Year") + if fiscal_year_list: + return fiscal_year_list[0].name + return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 13709e370bb..429a8a897c4 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -17,7 +17,12 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Asset Value Analytics", + "label": "Asset Value Analytics" + } + ], "creation": "2020-03-02 15:43:27.634865", "developer_mode_only": 0, "disable_user_customization": 0, @@ -27,7 +32,7 @@ "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-08 16:07:04.671296", + "modified": "2020-05-12 17:35:14.770662", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -42,8 +47,8 @@ "type": "DocType" }, { - "label": "Asset Movement", - "link_to": "Asset Movement", + "label": "Asset Category", + "link_to": "Asset Category", "type": "DocType" }, { diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index e886a35e5d3..a08851743cf 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -21,14 +21,61 @@ frappe.query_reports["Fixed Asset Register"] = { reqd: 1 }, { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" + "fieldname":"filter_based_on", + "label": __("Period Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } }, { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" + "fieldname":"from_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"from_fiscal_year", + "label": __("Start Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"to_fiscal_year", + "label": __("End Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"date_based_on", + "label": __("Date Based On"), + "fieldtype": "Select", + "options": ["Purchase Date", "Available For Use Date"], + "default": "Purchase Date", + "reqd": 1 }, { fieldname:"asset_category", @@ -48,18 +95,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Cost Center" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"group_by", label: __("Group By"), fieldtype: "Select", - options: " \nAsset Category\nLocation", - default: '', + options: ["--Select a group--", "Asset Category", "Location"], + default: "--Select a group--", + reqd: 1 }, { fieldname:"is_existing_asset", diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 0b4b3343264..23714e6f66f 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -4,26 +4,33 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr, today, flt +from frappe.utils import cstr, today, flt, add_years, formatdate, getdate +from erpnext.accounts.report.financial_statements import get_period_list, get_fiscal_year_data, validate_fiscal_year def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data) if not filters.get("group_by") else {} + chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {} return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status + date_field = frappe.scrub(filters.date_based_on or "Purchase Date") if filters.get('company'): conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.filter_based_on == "Date Range": + conditions[date_field] = ["between", [filters.from_date, filters.to_date]] + if filters.filter_based_on == "Fiscal Year": + fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) + validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) + year_start_date = getdate(fiscal_year.year_start_date) + year_end_date = getdate(fiscal_year.year_end_date) + + conditions[date_field] = ["between", [year_start_date, year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -49,16 +56,17 @@ def get_data(filters): pi_supplier_map = get_purchase_invoice_supplier_map() conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by") or "") - if group_by: - if group_by == "asset_category": - fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] - else: - fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + group_by = frappe.scrub(filters.get("group_by")) + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) - print(assets_record) + + elif group_by == "location": + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + else: fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", @@ -89,19 +97,29 @@ def get_data(filters): return data -def prepare_chart_data(data): - labels, asset_values, depreciated_amounts = [], [], [] +def prepare_chart_data(data, filters): + labels_values_map = {} + date_field = frappe.scrub(filters.date_based_on) + + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company) + + for d in period_list: + labels_values_map.setdefault(d.get('label'), frappe._dict({'asset_value': 0, 'depreciated_amount': 0})) + for d in data: - labels.append(d.get("asset_id")) - asset_values.append(d.get("asset_value")) - depreciated_amounts.append(d.get("depreciated_amount")) + date = d.get(date_field) + belongs_to_month = formatdate(date, "MMM YYYY") + + labels_values_map[belongs_to_month].asset_value += d.get("asset_value") + labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount") return { "data" : { - "labels": labels, + "labels": labels_values_map.keys(), "datasets": [ - { 'name': _('Asset Value'), 'values': asset_values }, - { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + { 'name': _('Asset Value'), 'values': [d.get("asset_value") for d in labels_values_map.values()] }, + { 'name': _('Depreciatied Amount'), 'values': [d.get("depreciated_amount") for d in labels_values_map.values()] } ] }, "type": "bar", @@ -144,7 +162,7 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): - if filters.get("group_by"): + if filters.get("group_by") in ["Asset Category", "Location"]: return [ { "label": _("{}").format(filters.get("group_by")), From 37c3bd28928ba0510429f8b015ad0fe8e2affba9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 14:10:05 +0530 Subject: [PATCH 232/410] chore: rename onboarding to module onboarding --- .../assets/{onboarding => module_onboarding}/assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename erpnext/assets/{onboarding => module_onboarding}/assets/assets.json (95%) diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json similarity index 95% rename from erpnext/assets/onboarding/assets/assets.json rename to erpnext/assets/module_onboarding/assets/assets.json index b32a7f2be5b..dc77726d28a 100644 --- a/erpnext/assets/onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -9,7 +9,7 @@ ], "creation": "2020-05-08 15:10:45.571457", "docstatus": 0, - "doctype": "Onboarding", + "doctype": "Module Onboarding", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", "idx": 0, "is_complete": 0, From 7c5e22e9d89b947061d3f94565621e4f8d3237fa Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 15:24:29 +0530 Subject: [PATCH 233/410] fix: depreciation amount not shown in asset register --- .../fixed_asset_register/fixed_asset_register.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 23714e6f66f..af08a2a601e 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -27,10 +27,10 @@ def get_conditions(filters): if filters.filter_based_on == "Fiscal Year": fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) - year_start_date = getdate(fiscal_year.year_start_date) - year_end_date = getdate(fiscal_year.year_end_date) + filters.year_start_date = getdate(fiscal_year.year_start_date) + filters.year_end_date = getdate(fiscal_year.year_end_date) - conditions[date_field] = ["between", [year_start_date, year_end_date]] + conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -51,12 +51,11 @@ def get_data(filters): data = [] + conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by")) if group_by == "asset_category": @@ -86,7 +85,7 @@ def get_data(filters): "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, @@ -129,7 +128,7 @@ def prepare_chart_data(data, filters): } def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date return frappe._dict(frappe.db.sql(''' Select parent, SUM(depreciation_amount) From 4f024128af7b796c6c5a863404a82332d7605f7d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 14 May 2020 15:33:37 +0530 Subject: [PATCH 234/410] fix: dashboard chart dialog filters --- .../fixed_asset_register.js | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a08851743cf..1a6ef54a830 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -26,31 +26,22 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Select", "options": ["Fiscal Year", "Date Range"], "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); - - frappe.query_report.refresh(); - } + "reqd": 1 }, { "fieldname":"from_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), - "hidden": 1, + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { "fieldname":"to_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), - "hidden": 1, + "default": frappe.datetime.nowdate(), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { @@ -59,6 +50,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { @@ -67,6 +59,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { From 054aafa1d7c10ed91798e66de34fbe866943f942 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 18:21:51 +0530 Subject: [PATCH 235/410] fix: Assets module onboarding and dashboard --- erpnext/assets/dashboard_fixtures.py | 69 +++++++++++++------ erpnext/assets/desk_page/assets/assets.json | 8 ++- .../module_onboarding/assets/assets.json | 7 +- .../create_an_asset.json} | 4 +- .../create_an_asset_category.json | 16 +++++ .../purchase_an_asset_item.json} | 4 +- 6 files changed, 79 insertions(+), 29 deletions(-) rename erpnext/assets/onboarding_step/{create_the_asset/create_the_asset.json => create_an_asset/create_an_asset.json} (84%) create mode 100644 erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json rename erpnext/assets/onboarding_step/{purchase_the_asset_item/purchase_the_asset_item.json => purchase_an_asset_item/purchase_an_asset_item.json} (82%) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 84e67cf648e..9af45d16b62 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,7 +3,10 @@ import frappe import json -from frappe.utils import nowdate, add_months +from frappe.utils import nowdate, add_months, get_date_str +from frappe import _ +from erpnext.accounts.utils import get_fiscal_year + def get_data(): return frappe._dict({ @@ -20,17 +23,25 @@ def get_dashboards(): { "chart": "Asset Value Analytics", "width": "Full" }, { "chart": "Category-wise Asset Value", "width": "Half" }, { "chart": "Location-wise Asset Value", "width": "Half" }, - ] + ], + "cards": [ + {"card": "Total Assets"}, + {"card": "New Assets (This Year)"}, + {"card": "Asset Value"} + ] }] +fiscal_year = get_fiscal_year(date=nowdate()) +year_start_date = get_date_str(fiscal_year[1]) +year_end_date = get_date_str(fiscal_year[2]) + + def get_charts(): company = get_company_for_dashboards() - fiscal_year = get_fiscal_year() - return [ { "name": "Asset Value Analytics", - "chart_name": "Asset Value Analytics", + "chart_name": _("Asset Value Analytics"), "chart_type": "Report", "report_name": "Fixed Asset Register", "is_custom": 1, @@ -44,10 +55,10 @@ def get_charts(): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year, - "to_fiscal_year": fiscal_year, - "period_start_date": add_months(nowdate(), -12), - "period_end_date": nowdate(), + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], + "period_start_date": year_start_date, + "period_end_date": year_end_date, "date_based_on": "Purchase Date", "group_by": "--Select a group--" }), @@ -63,7 +74,7 @@ def get_charts(): }, { "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", + "chart_name": _("Category-wise Asset Value"), "chart_type": "Report", "report_name": "Fixed Asset Register", "x_field": "asset_category", @@ -125,9 +136,33 @@ def get_charts(): def get_number_cards(): return [ + { + "name": "Total Assets", + "label": _("Total Assets"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + }, + { + "name": "New Assets (This Year)", + "label": _("New Assets (This Year)"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": json.dumps([ + ['Asset', 'creation', 'between', [year_start_date, year_end_date]] + ]), + "doctype": "Number Card", + }, { "name": "Asset Value", - "label": "Asset Value", + "label": _("Asset Value"), "function": "Sum", "aggregate_function_based_on": "value_after_depreciation", "document_type": "Asset", @@ -135,7 +170,7 @@ def get_number_cards(): "show_percentage_stats": 1, "stats_time_interval": "Monthly", "filters_json": "[]", - "doctype": "Number Card", + "doctype": "Number Card" } ] @@ -147,14 +182,4 @@ def get_company_for_dashboards(): company_list = frappe.get_list("Company") if company_list: return company_list[0].name - return None - -def get_fiscal_year(): - fiscal_year = frappe.defaults.get_defaults().fiscal_year - if fiscal_year: - return fiscal_year - else: - fiscal_year_list = frappe.get_list("Fiscal Year") - if fiscal_year_list: - return fiscal_year_list[0].name return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 429a8a897c4..94939fdd2a2 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -29,10 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-12 17:35:14.770662", + "modified": "2020-05-20 18:05:23.994795", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -55,6 +56,11 @@ "label": "Fixed Asset Register", "link_to": "Fixed Asset Register", "type": "Report" + }, + { + "label": "Assets Dashboard", + "link_to": "Asset", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json index dc77726d28a..66dd60ae81f 100644 --- a/erpnext/assets/module_onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -26,10 +26,13 @@ "step": "Create a Fixed Asset Item" }, { - "step": "Purchase the Asset Item" + "step": "Create an Asset Category" + }, + { + "step": "Purchase an Asset Item" }, { - "step": "Create the Asset" + "step": "Create an Asset" } ], "subtitle": "Assets, Depreciations, Repairs and more", diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json similarity index 84% rename from erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json rename to erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json index 28d8485f976..5488b1d7b4e 100644 --- a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json +++ b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:53.332538", "modified_by": "Administrator", - "name": "Create the Asset", + "name": "Create an Asset", "owner": "Administrator", "reference_document": "Asset", - "title": "Create the Asset" + "title": "Create an Asset" } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json new file mode 100644 index 00000000000..3bf54af348a --- /dev/null +++ b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create an Asset Category", + "owner": "Administrator", + "reference_document": "Asset Category", + "title": "Create an Asset Category" + } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json similarity index 82% rename from erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json rename to erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json index b6f38decb9e..732ff7f733b 100644 --- a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json +++ b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:28.208059", "modified_by": "Administrator", - "name": "Purchase the Asset Item", + "name": "Purchase an Asset Item", "owner": "Administrator", "reference_document": "Purchase Receipt", - "title": "Purchase the Asset Item" + "title": "Purchase an Asset Item" } \ No newline at end of file From 858593b96e9e76dfacb23c0742e29ed3de1dac67 Mon Sep 17 00:00:00 2001 From: anoop Date: Wed, 20 May 2020 15:41:37 +0530 Subject: [PATCH 236/410] fix: service unit create - set fields based on service unit type, added validations --- .../healthcare_service_unit.json | 19 +++++++++++++------ .../healthcare_service_unit.py | 10 ++++++++-- .../healthcare_service_unit_type.json | 16 ++++++++-------- .../healthcare_service_unit_type.py | 14 ++++++++++++++ 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index ea4ae846f7d..9ee865a62a4 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -12,7 +12,6 @@ "engine": "InnoDB", "field_order": [ "healthcare_service_unit_name", - "parent_healthcare_service_unit", "is_group", "service_unit_type", "allow_appointments", @@ -20,8 +19,10 @@ "inpatient_occupancy", "occupancy_status", "column_break_9", - "warehouse", "company", + "warehouse", + "tree_details_section", + "parent_healthcare_service_unit", "lft", "rgt", "old_parent" @@ -51,7 +52,6 @@ "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1", "fieldname": "is_group", "fieldtype": "Check", - "in_list_view": 1, "label": "Is Group" }, { @@ -63,12 +63,12 @@ "options": "Healthcare Service Unit Type" }, { - "bold": 1, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1", "fetch_from": "service_unit_type.allow_appointments", "fieldname": "allow_appointments", "fieldtype": "Check", + "in_list_view": 1, "label": "Allow Appointments", "no_copy": 1, "read_only": 1 @@ -90,6 +90,7 @@ "fetch_from": "service_unit_type.inpatient_occupancy", "fieldname": "inpatient_occupancy", "fieldtype": "Check", + "in_list_view": 1, "label": "Inpatient Occupancy", "no_copy": 1, "read_only": 1, @@ -101,7 +102,7 @@ "fieldtype": "Select", "label": "Occupancy Status", "no_copy": 1, - "options": "\nVacant\nOccupied", + "options": "Vacant\nOccupied", "read_only": 1 }, { @@ -157,10 +158,16 @@ "options": "Healthcare Service Unit", "print_hide": 1, "report_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "tree_details_section", + "fieldtype": "Section Break", + "label": "Tree Details" } ], "links": [], - "modified": "2020-03-26 16:13:08.675952", + "modified": "2020-05-20 18:26:56.065543", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py index 13cc43d2be4..9720078e32e 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py @@ -27,5 +27,11 @@ class HealthcareServiceUnit(NestedSet): self.allow_appointments = 0 self.overlap_appointments = 0 self.inpatient_occupancy = 0 - elif not self.allow_appointments: - self.overlap_appointments = 0 + elif self.service_unit_type: + service_unit_type = frappe.get_doc('Healthcare Service Unit Type', self.service_unit_type) + self.allow_appointments = service_unit_type.allow_appointments + self.overlap_appointments = service_unit_type.overlap_appointments + self.inpatient_occupancy = service_unit_type.inpatient_occupancy + if self.inpatient_occupancy: + self.occupancy_status = 'Vacant' + self.overlap_appointments = 0 diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index 5fa47d91bc2..4b8503d0286 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -31,6 +31,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Service Unit Type", + "no_copy": 1, "reqd": 1, "unique": 1 }, @@ -40,8 +41,7 @@ "depends_on": "eval:doc.inpatient_occupancy != 1", "fieldname": "allow_appointments", "fieldtype": "Check", - "label": "Allow Appointments", - "no_copy": 1 + "label": "Allow Appointments" }, { "bold": 1, @@ -49,8 +49,7 @@ "depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1", "fieldname": "overlap_appointments", "fieldtype": "Check", - "label": "Allow Overlap", - "no_copy": 1 + "label": "Allow Overlap" }, { "bold": 1, @@ -58,8 +57,7 @@ "depends_on": "eval:doc.allow_appointments != 1", "fieldname": "inpatient_occupancy", "fieldtype": "Check", - "label": "Inpatient Occupancy", - "no_copy": 1 + "label": "Inpatient Occupancy" }, { "bold": 1, @@ -79,6 +77,7 @@ "fieldname": "item", "fieldtype": "Link", "label": "Item", + "no_copy": 1, "options": "Item", "read_only": 1 }, @@ -86,7 +85,8 @@ "fieldname": "item_code", "fieldtype": "Data", "label": "Item Code", - "mandatory_depends_on": "eval: doc.is_billable == 1" + "mandatory_depends_on": "eval: doc.is_billable == 1", + "no_copy": 1 }, { "fieldname": "item_group", @@ -138,7 +138,7 @@ } ], "links": [], - "modified": "2020-01-30 16:06:00.624496", + "modified": "2020-05-20 15:31:09.627516", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index 286ecc0ff8e..a99358cdc60 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -10,6 +10,20 @@ from frappe.model.rename_doc import rename_doc class HealthcareServiceUnitType(Document): def validate(self): + if self.allow_appointments and self.inpatient_occupancy: + frappe.msgprint( + _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + raise_exception=1, title=_('Validation Error'), indicator='red' + ) + elif not self.allow_appointments and not self.inpatient_occupancy: + frappe.msgprint( + _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + raise_exception=1, title=_('Validation Error'), indicator='red' + ) + + if not self.allow_appointments: + self.overlap_appointments = 0 + if self.is_billable: if self.disabled: frappe.db.set_value('Item', self.item, 'disabled', 1) From 29c748184c7e5fc2df43176ae398ab7f7f83cebe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 237/410] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e608..5a699b6580a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) From 53257833f8e2f672c572e98bf1bb7af48e903985 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 00:46:40 +0530 Subject: [PATCH 238/410] feat: admission and discharge schedule detials via dialog --- .../inpatient_record/inpatient_record.js | 66 ++++-- .../inpatient_record/inpatient_record.json | 219 ++++++++++++++++-- .../inpatient_record/inpatient_record.py | 101 +++++--- .../patient_encounter/patient_encounter.js | 125 ++++++++-- .../patient_encounter/patient_encounter.json | 4 +- 5 files changed, 417 insertions(+), 98 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index 67c12f6c14d..b640239b701 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -2,22 +2,37 @@ // For license information, please see license.txt frappe.ui.form.on('Inpatient Record', { + setup: function(frm) { + frm.get_field('drug_prescription').grid.editable_fields = [ + {fieldname: 'drug_code', columns: 2}, + {fieldname: 'drug_name', columns: 2}, + {fieldname: 'dosage', columns: 2}, + {fieldname: 'period', columns: 2} + ]; + }, refresh: function(frm) { - if(!frm.doc.__islocal && frm.doc.status == "Admission Scheduled"){ + if(!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { + frm.enable_save(); + } else { + frm.disable_save(); + } + + if(!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { frm.add_custom_button(__('Admit'), function() { admit_patient_dialog(frm); } ); - frm.set_df_property("btn_transfer", "hidden", 1); } - if(!frm.doc.__islocal && frm.doc.status == "Discharge Scheduled"){ + + if(!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { frm.add_custom_button(__('Discharge'), function() { discharge_patient(frm); } ); - frm.set_df_property("btn_transfer", "hidden", 0); } - if(!frm.doc.__islocal && (frm.doc.status == "Discharged" || frm.doc.status == "Discharge Scheduled")){ + if(!frm.doc.__islocal && frm.doc.status != 'Admitted') { frm.disable_save(); - frm.set_df_property("btn_transfer", "hidden", 1); + frm.set_df_property('btn_transfer', 'hidden', 1); + } else { + frm.set_df_property('btn_transfer', 'hidden', 0); } }, btn_transfer: function(frm) { @@ -28,14 +43,14 @@ frappe.ui.form.on('Inpatient Record', { var discharge_patient = function(frm) { frappe.call({ doc: frm.doc, - method: "discharge", + method: 'discharge', callback: function(data) { if(!data.exc){ frm.reload_doc(); } }, freeze: true, - freeze_message: "Process Discharge" + freeze_message: 'Processing Inpatient Discharge' }); }; @@ -44,12 +59,20 @@ var admit_patient_dialog = function(frm){ title: 'Admit Patient', width: 100, fields: [ - {fieldtype: "Link", label: "Service Unit Type", fieldname: "service_unit_type", options: "Healthcare Service Unit Type"}, - {fieldtype: "Link", label: "Service Unit", fieldname: "service_unit", options: "Healthcare Service Unit", reqd: 1}, - {fieldtype: "Datetime", label: "Admission Datetime", fieldname: "check_in", reqd: 1}, - {fieldtype: "Date", label: "Expected Discharge", fieldname: "expected_discharge"} + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', + options: 'Healthcare Service Unit Type', default: frm.doc.admission_service_unit_type + }, + {fieldtype: 'Link', label: 'Service Unit', fieldname: 'service_unit', + options: 'Healthcare Service Unit', reqd: 1 + }, + {fieldtype: 'Datetime', label: 'Admission Datetime', fieldname: 'check_in', + reqd: 1, default: frappe.datetime.now_datetime() + }, + {fieldtype: 'Date', label: 'Expected Discharge', fieldname: 'expected_discharge', + default: frm.doc.expected_length_of_stay ? frappe.datetime.add_days(frappe.datetime.now_datetime(), frm.doc.expected_length_of_stay) : '' + } ], - primary_action_label: __("Admit"), + primary_action_label: __('Admit'), primary_action : function(){ var service_unit = dialog.get_value('service_unit'); var check_in = dialog.get_value('check_in'); @@ -74,27 +97,28 @@ var admit_patient_dialog = function(frm){ } }, freeze: true, - freeze_message: "Process Admission" + freeze_message: 'Processing Patient Admission' }); frm.refresh_fields(); dialog.hide(); } }); - dialog.fields_dict["service_unit_type"].get_query = function(){ + dialog.fields_dict['service_unit_type'].get_query = function() { return { filters: { - "inpatient_occupancy": 1, - "allow_appointments": 0 + 'inpatient_occupancy': 1, + 'allow_appointments': 0 } }; }; - dialog.fields_dict["service_unit"].get_query = function(){ + dialog.fields_dict['service_unit'].get_query = function() { return { filters: { - "is_group": 0, - "service_unit_type": dialog.get_value("service_unit_type"), - "occupancy_status" : "Vacant" + 'is_group': 0, + 'company': frm.doc.company, + 'service_unit_type': dialog.get_value('service_unit_type'), + 'occupancy_status' : 'Vacant' } }; }; diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index c1b516d5363..d3835409d9a 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -22,17 +22,41 @@ "scheduled_date", "admitted_datetime", "expected_discharge", - "discharge_date", "references", - "cb_admission", - "admission_practitioner", "admission_encounter", - "cb_discharge", - "discharge_practitioner", - "discharge_encounter", + "admission_practitioner", + "medical_department", + "admission_ordered_for", + "expected_length_of_stay", + "admission_service_unit_type", + "cb_admission", + "primary_practitioner", + "secondary_practitioner", + "admission_instruction", + "encounter_details_section", + "chief_complaint", + "column_break_29", + "diagnosis", + "medication_section", + "drug_prescription", + "investigations_section", + "lab_test_prescription", + "procedures_section", + "procedure_prescription", + "rehabilitation_section", + "therapy_plan", + "therapies", "sb_inpatient_occupancy", "inpatient_occupancies", "btn_transfer", + "sb_discharge_details", + "discharge_ordered_date", + "discharge_practitioner", + "discharge_encounter", + "discharge_date", + "cb_discharge", + "discharge_instructions", + "followup_date", "sb_discharge_note", "discharge_note" ], @@ -54,7 +78,8 @@ "in_list_view": 1, "label": "Patient", "options": "Patient", - "reqd": 1 + "reqd": 1, + "set_only_once": 1 }, { "fetch_from": "patient.patient_name", @@ -108,11 +133,31 @@ "label": "Phone", "read_only": 1 }, + { + "fieldname": "medical_department", + "fieldtype": "Link", + "label": "Medical Department", + "options": "Medical Department", + "set_only_once": 1 + }, + { + "fieldname": "primary_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner (Primary)", + "options": "Healthcare Practitioner" + }, + { + "fieldname": "secondary_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner (Secondary)", + "options": "Healthcare Practitioner" + }, { "fieldname": "column_break_8", "fieldtype": "Column Break" }, { + "default": "Admission Scheduled", "fieldname": "status", "fieldtype": "Select", "in_list_view": 1, @@ -126,37 +171,45 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Admission Schedule Date", + "read_only": 1, "reqd": 1 }, { - "default": "Today", + "fieldname": "admission_ordered_for", + "fieldtype": "Date", + "label": "Admission Ordered For", + "read_only": 1 + }, + { "fieldname": "admitted_datetime", "fieldtype": "Datetime", "in_list_view": 1, - "label": "Admitted Datetime" + "label": "Admitted Datetime", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.expected_length_of_stay > 0)", + "fieldname": "expected_length_of_stay", + "fieldtype": "Int", + "label": "Expected Length of Stay", + "set_only_once": 1 }, { "fieldname": "expected_discharge", "fieldtype": "Date", "in_list_view": 1, - "label": "Expected Discharge" - }, - { - "fieldname": "discharge_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Discharge Date" + "label": "Expected Discharge", + "read_only": 1 }, { "collapsible": 1, "fieldname": "references", "fieldtype": "Section Break", - "label": "References" + "label": "Admission Order Details" }, { "fieldname": "cb_admission", - "fieldtype": "Column Break", - "label": "Admission" + "fieldtype": "Column Break" }, { "fieldname": "admission_practitioner", @@ -172,10 +225,21 @@ "options": "Patient Encounter", "read_only": 1 }, + { + "fieldname": "chief_complaint", + "fieldtype": "Table MultiSelect", + "label": "Chief Complaint", + "options": "Patient Encounter Symptom" + }, + { + "fieldname": "admission_instruction", + "fieldtype": "Small Text", + "label": "Admission Instruction", + "set_only_once": 1 + }, { "fieldname": "cb_discharge", - "fieldtype": "Column Break", - "label": "Discharge" + "fieldtype": "Column Break" }, { "fieldname": "discharge_practitioner", @@ -192,10 +256,51 @@ "read_only": 1 }, { + "collapsible": 1, + "fieldname": "medication_section", + "fieldtype": "Section Break", + "label": "Medications" + }, + { + "fieldname": "drug_prescription", + "fieldtype": "Table", + "options": "Drug Prescription" + }, + { + "collapsible": 1, + "fieldname": "investigations_section", + "fieldtype": "Section Break", + "label": "Investigations" + }, + { + "fieldname": "lab_test_prescription", + "fieldtype": "Table", + "options": "Lab Prescription" + }, + { + "collapsible": 1, + "fieldname": "procedures_section", + "fieldtype": "Section Break", + "label": "Procedures" + }, + { + "fieldname": "procedure_prescription", + "fieldtype": "Table", + "options": "Procedure Prescription" + }, + { + "depends_on": "eval:(doc.status != \"Admission Scheduled\")", "fieldname": "sb_inpatient_occupancy", "fieldtype": "Section Break", "label": "Inpatient Occupancy" }, + { + "fieldname": "admission_service_unit_type", + "fieldtype": "Link", + "label": "Admission Service Unit Type", + "options": "Healthcare Service Unit Type", + "read_only": 1 + }, { "fieldname": "inpatient_occupancies", "fieldtype": "Table", @@ -208,10 +313,10 @@ "label": "Transfer" }, { - "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")", "fieldname": "sb_discharge_note", "fieldtype": "Section Break", - "label": "Discharge Note" + "label": "Discharge Notes" }, { "fieldname": "discharge_note", @@ -224,10 +329,76 @@ "in_standard_filter": 1, "label": "Company", "options": "Company" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:(doc.status == \"Admitted\")", + "fieldname": "encounter_details_section", + "fieldtype": "Section Break", + "label": "Encounter Impression" + }, + { + "fieldname": "column_break_29", + "fieldtype": "Column Break" + }, + { + "fieldname": "diagnosis", + "fieldtype": "Table MultiSelect", + "label": "Diagnosis", + "options": "Patient Encounter Diagnosis" + }, + { + "fieldname": "followup_date", + "fieldtype": "Date", + "label": "Follow Up Date" + }, + { + "collapsible": 1, + "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")", + "fieldname": "sb_discharge_details", + "fieldtype": "Section Break", + "label": "Discharge Detials" + }, + { + "fieldname": "discharge_instructions", + "fieldtype": "Small Text", + "label": "Discharge Instructions" + }, + { + "fieldname": "discharge_ordered_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Ordered Date", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "rehabilitation_section", + "fieldtype": "Section Break", + "label": "Rehabilitation" + }, + { + "fieldname": "therapy_plan", + "fieldtype": "Link", + "hidden": 1, + "label": "Therapy Plan", + "options": "Therapy Plan", + "read_only": 1 + }, + { + "fieldname": "therapies", + "fieldtype": "Table", + "options": "Therapy Plan Detail" + }, + { + "fieldname": "discharge_date", + "fieldtype": "Date", + "label": "Discharge Date", + "read_only": 1 } ], "links": [], - "modified": "2020-04-07 13:13:39.351977", + "modified": "2020-05-21 00:37:12.939925", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Record", diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 835b38bedf8..e668204dcf4 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, json from frappe import _ from frappe.utils import today, now_datetime, getdate from frappe.model.document import Document @@ -11,8 +11,12 @@ from frappe.desk.reportview import get_match_cond class InpatientRecord(Document): def after_insert(self): - frappe.db.set_value("Patient", self.patient, "inpatient_status", "Admission Scheduled") - frappe.db.set_value("Patient", self.patient, "inpatient_record", self.name) + frappe.db.set_value('Patient', self.patient, 'inpatient_record', self.name) + frappe.db.set_value('Patient', self.patient, 'inpatient_status', self.status) + + if self.admission_encounter: # Update encounter + frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_record', self.name) + frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_status', self.status) def validate(self): self.validate_dates() @@ -26,7 +30,7 @@ class InpatientRecord(Document): (getdate(self.admitted_datetime) < getdate(today())): frappe.throw(_("Scheduled and Admitted dates can not be less than today")) if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ - (getdate(self.discharge_date) < getdate(self.scheduled_date)): + (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): frappe.throw(_("Expected and Discharge dates cannot be less than Admission Schedule date")) def validate_already_scheduled_or_admitted(self): @@ -59,37 +63,76 @@ class InpatientRecord(Document): if service_unit: transfer_patient(self, service_unit, check_in) + @frappe.whitelist() -def schedule_inpatient(patient, encounter_id, practitioner): - patient_obj = frappe.get_doc('Patient', patient) +def schedule_inpatient(args): + admission_order = json.loads(args) # admission order via Encounter + if not admission_order or not admission_order['patient'] or not admission_order['admission_encounter']: + frappe.throw(_('Missing required details, did not create Inpatient Record')) + inpatient_record = frappe.new_doc('Inpatient Record') - inpatient_record.patient = patient - inpatient_record.patient_name = patient_obj.patient_name - inpatient_record.gender = patient_obj.sex - inpatient_record.blood_group = patient_obj.blood_group - inpatient_record.dob = patient_obj.dob - inpatient_record.mobile = patient_obj.mobile - inpatient_record.email = patient_obj.email - inpatient_record.phone = patient_obj.phone - inpatient_record.status = "Admission Scheduled" + + # Admission order details + set_details_from_ip_order(inpatient_record, admission_order) + + # Patient details + patient = frappe.get_doc('Patient', admission_order['patient']) + inpatient_record.patient = patient.name + inpatient_record.patient_name = patient.patient_name + inpatient_record.gender = patient.sex + inpatient_record.blood_group = patient.blood_group + inpatient_record.dob = patient.dob + inpatient_record.mobile = patient.mobile + inpatient_record.email = patient.email + inpatient_record.phone = patient.phone inpatient_record.scheduled_date = today() - inpatient_record.admission_practitioner = practitioner - inpatient_record.admission_encounter = encounter_id + + # Set encounter detials + encounter = frappe.get_doc('Patient Encounter', admission_order['admission_encounter']) + if encounter and encounter.symptoms: # Symptoms + set_ip_child_records(inpatient_record, 'chief_complaint', encounter.symptoms) + + if encounter and encounter.diagnosis: # Diagnosis + set_ip_child_records(inpatient_record, 'diagnosis', encounter.diagnosis) + + if encounter and encounter.drug_prescription: # Medication + set_ip_child_records(inpatient_record, 'drug_prescription', encounter.drug_prescription) + + if encounter and encounter.lab_test_prescription: # Lab Tests + set_ip_child_records(inpatient_record, 'lab_test_prescription', encounter.lab_test_prescription) + + if encounter and encounter.procedure_prescription: # Procedure Prescription + set_ip_child_records(inpatient_record, 'procedure_prescription', encounter.procedure_prescription) + + if encounter and encounter.therapies: # Therapies + inpatient_record.therapy_plan = encounter.therapy_plan + set_ip_child_records(inpatient_record, 'therapies', encounter.therapies) + + inpatient_record.status = 'Admission Scheduled' inpatient_record.save(ignore_permissions = True) @frappe.whitelist() -def schedule_discharge(patient, encounter_id=None, practitioner=None): - inpatient_record_id = frappe.db.get_value('Patient', patient, 'inpatient_record') +def schedule_discharge(args): + discharge_order = json.loads(args) + inpatient_record_id = frappe.db.get_value('Patient', discharge_order['patient'], 'inpatient_record') if inpatient_record_id: - inpatient_record = frappe.get_doc("Inpatient Record", inpatient_record_id) - inpatient_record.discharge_practitioner = practitioner - inpatient_record.discharge_encounter = encounter_id - inpatient_record.status = "Discharge Scheduled" - + inpatient_record = frappe.get_doc('Inpatient Record', inpatient_record_id) check_out_inpatient(inpatient_record) - + set_details_from_ip_order(inpatient_record, discharge_order) + inpatient_record.status = 'Discharge Scheduled' inpatient_record.save(ignore_permissions = True) - frappe.db.set_value("Patient", patient, "inpatient_status", "Discharge Scheduled") + frappe.db.set_value('Patient', discharge_order['patient'], 'inpatient_status', inpatient_record.status) + frappe.db.set_value('Patient Encounter', inpatient_record.discharge_encounter, 'inpatient_status', inpatient_record.status) + +def set_details_from_ip_order(inpatient_record, ip_order): + for key in ip_order: + inpatient_record.set(key, ip_order[key]) + +def set_ip_child_records(inpatient_record, inpatient_record_child, encounter_child): + for item in encounter_child: + table = inpatient_record.append(inpatient_record_child) + for df in table.meta.get('fields'): + table.set(df.fieldname, item.get(df.fieldname)) def check_out_inpatient(inpatient_record): if inpatient_record.inpatient_occupancies: @@ -149,14 +192,14 @@ def get_inpatient_docs_not_invoiced(doc, inpatient_record): def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None): inpatient_record.admitted_datetime = check_in - inpatient_record.status = "Admitted" + inpatient_record.status = 'Admitted' inpatient_record.expected_discharge = expected_discharge inpatient_record.set('inpatient_occupancies', []) transfer_patient(inpatient_record, service_unit, check_in) - frappe.db.set_value("Patient", inpatient_record.patient, "inpatient_status", "Admitted") - frappe.db.set_value("Patient", inpatient_record.patient, "inpatient_record", inpatient_record.name) + frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_status', 'Admitted') + frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_record', inpatient_record.name) def transfer_patient(inpatient_record, service_unit, check_in): item_line = inpatient_record.append('inpatient_occupancies', {}) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 2410f8e10dd..43e43acda48 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -180,35 +180,114 @@ frappe.ui.form.on('Patient Encounter', { } }); -let schedule_inpatient = function(frm) { - frappe.call({ - method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient', - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if (!data.exc) { - frm.reload_doc(); +var schedule_inpatient = function(frm) { + var dialog = new frappe.ui.Dialog({ + title: 'Patient Admission', + fields: [ + {fieldtype: 'Link', label: 'Medical Department', fieldname: 'medical_department', options: 'Medical Department', reqd: 1}, + {fieldtype: 'Link', label: 'Healthcare Practitioner (Primary)', fieldname: 'primary_practitioner', options: 'Healthcare Practitioner', reqd: 1}, + {fieldtype: 'Link', label: 'Healthcare Practitioner (Secondary)', fieldname: 'secondary_practitioner', options: 'Healthcare Practitioner'}, + {fieldtype: 'Column Break'}, + {fieldtype: 'Date', label: 'Admission Ordered For', fieldname: 'admission_ordered_for', default: 'Today'}, + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, + {fieldtype: 'Int', label: 'Expected Length of Stay', fieldname: 'expected_length_of_stay'}, + {fieldtype: 'Section Break', label: 'Admission Instructions'}, + {fieldtype: 'Small Text', fieldname: 'admission_instruction'} + ], + primary_action_label: __('Order Admission'), + primary_action : function() { + var args = { + patient: frm.doc.patient, + admission_encounter: frm.doc.name, + referring_practitioner: frm.doc.practitioner, + company: frm.doc.company, + medical_department: dialog.get_value('medical_department'), + primary_practitioner: dialog.get_value('primary_practitioner'), + secondary_practitioner: dialog.get_value('secondary_practitioner'), + admission_ordered_for: dialog.get_value('admission_ordered_for'), + admission_service_unit_type: dialog.get_value('service_unit_type'), + expected_length_of_stay: dialog.get_value('expected_length_of_stay'), + admission_instruction: dialog.get_value('admission_instruction') } - }, - freeze: true, - freeze_message: __('Process Inpatient Scheduling') + frappe.call({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient', + args: { + args: args + }, + callback: function(data) { + if(!data.exc){ + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: 'Scheduling Patient Admission' + }); + frm.refresh_fields(); + dialog.hide(); + } }); + + dialog.set_values({ + 'medical_department': frm.doc.medical_department, + 'primary_practitioner': frm.doc.practitioner, + }); + + dialog.fields_dict['service_unit_type'].get_query = function() { + return { + filters: { + 'inpatient_occupancy': 1, + 'allow_appointments': 0 + } + }; + }; + + dialog.show(); + dialog.$wrapper.find('.modal-dialog').css('width', '800px'); }; -let schedule_discharge = function(frm) { - frappe.call({ - method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge', - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if (!data.exc) { - frm.reload_doc(); +var schedule_discharge = function(frm) { + var dialog = new frappe.ui.Dialog ({ + title: 'Inpatient Discharge', + fields: [ + {fieldtype: 'Date', label: 'Discharge Ordered Date', fieldname: 'discharge_ordered_date', default: 'Today', read_only: 1}, + {fieldtype: 'Date', label: 'Followup Date', fieldname: 'followup_date'}, + {fieldtype: 'Column Break'}, + {fieldtype: 'Small Text', label: 'Discharge Instructions', fieldname: 'discharge_instructions'}, + {fieldtype: 'Section Break', label:'Discharge Summary'}, + {fieldtype: 'Text Editor', label: 'Discharge Note', fieldname: 'discharge_note'} + ], + primary_action_label: __('Order Discharge'), + primary_action : function() { + var args = { + patient: frm.doc.patient, + discharge_encounter: frm.doc.name, + discharge_practitioner: frm.doc.practitioner, + discharge_ordered_date: dialog.get_value('discharge_ordered_date'), + followup_date: dialog.get_value('followup_date'), + discharge_instructions: dialog.get_value('discharge_instructions'), + discharge_note: dialog.get_value('discharge_note') } - }, - freeze: true, - freeze_message: 'Process Discharge' + frappe.call ({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge', + args: {args}, + callback: function(data) { + if(!data.exc){ + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: 'Scheduling Inpatient Discharge' + }); + frm.refresh_fields(); + dialog.hide(); + } }); + + dialog.show(); + dialog.$wrapper.find('.modal-dialog').css('width', '800px'); }; -let create_medical_record = function (frm) { +let create_medical_record = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } @@ -221,7 +300,7 @@ let create_medical_record = function (frm) { frappe.new_doc('Patient Medical Record'); }; -let create_vital_signs = function (frm) { +let create_vital_signs = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } @@ -233,7 +312,7 @@ let create_vital_signs = function (frm) { frappe.new_doc('Vital Signs'); }; -let create_procedure = function (frm) { +let create_procedure = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 05eec87398b..15675f4673f 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -52,6 +52,7 @@ ], "fields": [ { + "allow_on_submit": 1, "fieldname": "inpatient_record", "fieldtype": "Link", "label": "Inpatient Record", @@ -296,6 +297,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "fieldname": "inpatient_status", "fieldtype": "Data", "label": "Inpatient Status", @@ -326,7 +328,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 21:58:29.789797", + "modified": "2020-05-16 21:00:08.644531", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", From 4053c46609c7f1c3a47ed5c1420a621ef5c09a5f Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 01:31:48 +0530 Subject: [PATCH 239/410] fix: consider only submitted docs for invoicing --- .../healthcare/doctype/inpatient_record/inpatient_record.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index e668204dcf4..8056074668f 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -187,8 +187,8 @@ def get_pending_doc(doc, doc_name_list, pending_invoices): return pending_invoices def get_inpatient_docs_not_invoiced(doc, inpatient_record): - return frappe.db.get_list(doc, filters = {"patient": inpatient_record.patient, - "inpatient_record": inpatient_record.name, "invoiced": 0}) + return frappe.db.get_list(doc, filters = {'patient': inpatient_record.patient, + 'inpatient_record': inpatient_record.name, 'docstatus': 1, 'invoiced': 0}) def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None): inpatient_record.admitted_datetime = check_in From b53638c0a3216dfc7c27659d7a7ee5bf0a505443 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 01:34:18 +0530 Subject: [PATCH 240/410] fix: ip-order dialogs use long text field --- .../doctype/patient_encounter/patient_encounter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 43e43acda48..ef1068e6cb5 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -191,8 +191,8 @@ var schedule_inpatient = function(frm) { {fieldtype: 'Date', label: 'Admission Ordered For', fieldname: 'admission_ordered_for', default: 'Today'}, {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, {fieldtype: 'Int', label: 'Expected Length of Stay', fieldname: 'expected_length_of_stay'}, - {fieldtype: 'Section Break', label: 'Admission Instructions'}, - {fieldtype: 'Small Text', fieldname: 'admission_instruction'} + {fieldtype: 'Section Break'}, + {fieldtype: 'Long Text', label: 'Admission Instructions', fieldname: 'admission_instruction'} ], primary_action_label: __('Order Admission'), primary_action : function() { @@ -254,7 +254,7 @@ var schedule_discharge = function(frm) { {fieldtype: 'Column Break'}, {fieldtype: 'Small Text', label: 'Discharge Instructions', fieldname: 'discharge_instructions'}, {fieldtype: 'Section Break', label:'Discharge Summary'}, - {fieldtype: 'Text Editor', label: 'Discharge Note', fieldname: 'discharge_note'} + {fieldtype: 'Long Text', label: 'Discharge Note', fieldname: 'discharge_note'} ], primary_action_label: __('Order Discharge'), primary_action : function() { From c6ee83bf123f6ca15635c130c44a07910a3c2be9 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 02:26:55 +0530 Subject: [PATCH 241/410] fix: inpatient date validation removed, added role perms service unit defaults not set when created from tree, added validations on after_insert --- .../healthcare_service_unit.py | 2 +- .../inpatient_record/inpatient_record.json | 75 +++++++++++++++---- .../inpatient_record/inpatient_record.py | 5 +- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py index 9720078e32e..9e0417a2bef 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py @@ -22,7 +22,7 @@ class HealthcareServiceUnit(NestedSet): super(HealthcareServiceUnit, self).on_update() self.validate_one_root() - def validate(self): + def after_insert(self): if self.is_group: self.allow_appointments = 0 self.overlap_appointments = 0 diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index d3835409d9a..5ced845c1b0 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -229,7 +229,8 @@ "fieldname": "chief_complaint", "fieldtype": "Table MultiSelect", "label": "Chief Complaint", - "options": "Patient Encounter Symptom" + "options": "Patient Encounter Symptom", + "permlevel": 1 }, { "fieldname": "admission_instruction", @@ -259,34 +260,40 @@ "collapsible": 1, "fieldname": "medication_section", "fieldtype": "Section Break", - "label": "Medications" + "label": "Medications", + "permlevel": 1 }, { "fieldname": "drug_prescription", "fieldtype": "Table", - "options": "Drug Prescription" + "options": "Drug Prescription", + "permlevel": 1 }, { "collapsible": 1, "fieldname": "investigations_section", "fieldtype": "Section Break", - "label": "Investigations" + "label": "Investigations", + "permlevel": 1 }, { "fieldname": "lab_test_prescription", "fieldtype": "Table", - "options": "Lab Prescription" + "options": "Lab Prescription", + "permlevel": 1 }, { "collapsible": 1, "fieldname": "procedures_section", "fieldtype": "Section Break", - "label": "Procedures" + "label": "Procedures", + "permlevel": 1 }, { "fieldname": "procedure_prescription", "fieldtype": "Table", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "permlevel": 1 }, { "depends_on": "eval:(doc.status != \"Admission Scheduled\")", @@ -320,7 +327,8 @@ }, { "fieldname": "discharge_note", - "fieldtype": "Text Editor" + "fieldtype": "Text Editor", + "permlevel": 1 }, { "fetch_from": "admission_encounter.company", @@ -335,7 +343,8 @@ "collapsible_depends_on": "eval:(doc.status == \"Admitted\")", "fieldname": "encounter_details_section", "fieldtype": "Section Break", - "label": "Encounter Impression" + "label": "Encounter Impression", + "permlevel": 1 }, { "fieldname": "column_break_29", @@ -345,7 +354,8 @@ "fieldname": "diagnosis", "fieldtype": "Table MultiSelect", "label": "Diagnosis", - "options": "Patient Encounter Diagnosis" + "options": "Patient Encounter Diagnosis", + "permlevel": 1 }, { "fieldname": "followup_date", @@ -375,7 +385,8 @@ "collapsible": 1, "fieldname": "rehabilitation_section", "fieldtype": "Section Break", - "label": "Rehabilitation" + "label": "Rehabilitation", + "permlevel": 1 }, { "fieldname": "therapy_plan", @@ -383,12 +394,14 @@ "hidden": 1, "label": "Therapy Plan", "options": "Therapy Plan", + "permlevel": 1, "read_only": 1 }, { "fieldname": "therapies", "fieldtype": "Table", - "options": "Therapy Plan Detail" + "options": "Therapy Plan Detail", + "permlevel": 1 }, { "fieldname": "discharge_date", @@ -398,7 +411,7 @@ } ], "links": [], - "modified": "2020-05-21 00:37:12.939925", + "modified": "2020-05-21 02:26:22.144575", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Record", @@ -415,6 +428,42 @@ "role": "Healthcare Administrator", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Physician", + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Nursing User" } ], "restrict_to_domain": "Healthcare", diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 8056074668f..802ab414c0c 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -26,12 +26,9 @@ class InpatientRecord(Document): frappe.db.set_value("Patient", self.patient, "inpatient_record", None) def validate_dates(self): - if (getdate(self.scheduled_date) < getdate(today())) or \ - (getdate(self.admitted_datetime) < getdate(today())): - frappe.throw(_("Scheduled and Admitted dates can not be less than today")) if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): - frappe.throw(_("Expected and Discharge dates cannot be less than Admission Schedule date")) + frappe.throw(_('Expected and Discharge dates cannot be less than Admission Schedule date')) def validate_already_scheduled_or_admitted(self): query = """ From 94762ff4ffaa8b91e08213850ec80a8ef2415a5b Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 03:08:47 +0530 Subject: [PATCH 242/410] fix: invoiced field position, medical department field corrected in query --- .../doctype/patient_appointment/patient_appointment.json | 4 ++-- .../doctype/patient_appointment/patient_appointment.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index b8a400c6b7a..ac35acc21ac 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -39,9 +39,9 @@ "section_break_16", "mode_of_payment", "billing_item", + "invoiced", "column_break_2", "paid_amount", - "invoiced", "ref_sales_invoice", "section_break_3", "referring_practitioner", @@ -348,7 +348,7 @@ } ], "links": [], - "modified": "2020-04-27 21:36:06.404062", + "modified": "2020-05-21 03:04:21.400893", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 9eb6e77c855..512fb48360f 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -447,7 +447,7 @@ def get_prescribed_therapies(patient): """ SELECT t.therapy_type, t.name, t.parent, e.practitioner, - e.encounter_date, e.therapy_plan, e.visit_department + e.encounter_date, e.therapy_plan, e.medical_department FROM `tabPatient Encounter` e, `tabTherapy Plan Detail` t WHERE From d9d1f442df3414796ffeb7b6e1d86acb66fdd079 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 09:02:46 +0530 Subject: [PATCH 243/410] fix: service unit validation and translation --- .../healthcare_service_unit_type.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index a99358cdc60..bb86eaacc40 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -12,12 +12,14 @@ class HealthcareServiceUnitType(Document): def validate(self): if self.allow_appointments and self.inpatient_occupancy: frappe.msgprint( - _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + _('Healthcare Service Unit Type cannot have both {0} and {1}').format( + frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')), raise_exception=1, title=_('Validation Error'), indicator='red' ) elif not self.allow_appointments and not self.inpatient_occupancy: frappe.msgprint( - _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + _('Healthcare Service Unit Type must allow atleast one among {0} and {1}').format( + frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')), raise_exception=1, title=_('Validation Error'), indicator='red' ) From f3a2f1fe207fbc7f637bc26ef461f946eaa07a8d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 09:27:42 +0530 Subject: [PATCH 244/410] fix(ip): code cleanup and translations --- .../inpatient_record/inpatient_record.js | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index b640239b701..971e166067e 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -11,24 +11,24 @@ frappe.ui.form.on('Inpatient Record', { ]; }, refresh: function(frm) { - if(!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { + if (!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { frm.enable_save(); } else { frm.disable_save(); } - if(!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { + if (!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { frm.add_custom_button(__('Admit'), function() { admit_patient_dialog(frm); } ); } - if(!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { + if (!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { frm.add_custom_button(__('Discharge'), function() { discharge_patient(frm); } ); } - if(!frm.doc.__islocal && frm.doc.status != 'Admitted') { + if (!frm.doc.__islocal && frm.doc.status != 'Admitted') { frm.disable_save(); frm.set_df_property('btn_transfer', 'hidden', 1); } else { @@ -40,22 +40,22 @@ frappe.ui.form.on('Inpatient Record', { } }); -var discharge_patient = function(frm) { +let discharge_patient = function(frm) { frappe.call({ doc: frm.doc, method: 'discharge', callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: 'Processing Inpatient Discharge' + freeze_message: __('Processing Inpatient Discharge') }); }; -var admit_patient_dialog = function(frm){ - var dialog = new frappe.ui.Dialog({ +let admit_patient_dialog = function(frm) { + let dialog = new frappe.ui.Dialog({ title: 'Admit Patient', width: 100, fields: [ @@ -74,13 +74,13 @@ var admit_patient_dialog = function(frm){ ], primary_action_label: __('Admit'), primary_action : function(){ - var service_unit = dialog.get_value('service_unit'); - var check_in = dialog.get_value('check_in'); - var expected_discharge = null; - if(dialog.get_value('expected_discharge')){ + let service_unit = dialog.get_value('service_unit'); + let check_in = dialog.get_value('check_in'); + let expected_discharge = null; + if (dialog.get_value('expected_discharge')) { expected_discharge = dialog.get_value('expected_discharge'); } - if(!service_unit && !check_in){ + if (!service_unit && !check_in) { return; } frappe.call({ @@ -92,12 +92,12 @@ var admit_patient_dialog = function(frm){ 'expected_discharge': expected_discharge }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: 'Processing Patient Admission' + freeze_message: __('Processing Patient Admission') }); frm.refresh_fields(); dialog.hide(); @@ -126,21 +126,21 @@ var admit_patient_dialog = function(frm){ dialog.show(); }; -var transfer_patient_dialog = function(frm){ - var dialog = new frappe.ui.Dialog({ +let transfer_patient_dialog = function(frm) { + let dialog = new frappe.ui.Dialog({ title: 'Transfer Patient', width: 100, fields: [ - {fieldtype: "Link", label: "Leave From", fieldname: "leave_from", options: "Healthcare Service Unit", reqd: 1, read_only:1}, - {fieldtype: "Link", label: "Service Unit Type", fieldname: "service_unit_type", options: "Healthcare Service Unit Type"}, - {fieldtype: "Link", label: "Transfer To", fieldname: "service_unit", options: "Healthcare Service Unit", reqd: 1}, - {fieldtype: "Datetime", label: "Check In", fieldname: "check_in", reqd: 1} + {fieldtype: 'Link', label: 'Leave From', fieldname: 'leave_from', options: 'Healthcare Service Unit', reqd: 1, read_only:1}, + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, + {fieldtype: 'Link', label: 'Transfer To', fieldname: 'service_unit', options: 'Healthcare Service Unit', reqd: 1}, + {fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1} ], - primary_action_label: __("Transfer"), - primary_action : function(){ - var service_unit = null; - var check_in = dialog.get_value('check_in'); - var leave_from = null; + primary_action_label: __('Transfer'), + primary_action : function() { + let service_unit = null; + let check_in = dialog.get_value('check_in'); + let leave_from = null; if(dialog.get_value('leave_from')){ leave_from = dialog.get_value('leave_from'); } @@ -159,47 +159,47 @@ var transfer_patient_dialog = function(frm){ 'leave_from': leave_from }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: "Process Transfer" + freeze_message: __('Process Transfer') }); frm.refresh_fields(); dialog.hide(); } }); - dialog.fields_dict["leave_from"].get_query = function(){ + dialog.fields_dict['leave_from'].get_query = function(){ return { - query : "erpnext.healthcare.doctype.inpatient_record.inpatient_record.get_leave_from", + query : 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.get_leave_from', filters: {docname:frm.doc.name} }; }; - dialog.fields_dict["service_unit_type"].get_query = function(){ + dialog.fields_dict['service_unit_type'].get_query = function(){ return { filters: { - "inpatient_occupancy": 1, - "allow_appointments": 0 + 'inpatient_occupancy': 1, + 'allow_appointments': 0 } }; }; - dialog.fields_dict["service_unit"].get_query = function(){ + dialog.fields_dict['service_unit'].get_query = function(){ return { filters: { - "is_group": 0, - "service_unit_type": dialog.get_value("service_unit_type"), - "occupancy_status" : "Vacant" + 'is_group': 0, + 'service_unit_type': dialog.get_value('service_unit_type'), + 'occupancy_status' : 'Vacant' } }; }; dialog.show(); - var not_left_service_unit = null; - for(let inpatient_occupancy in frm.doc.inpatient_occupancies){ - if(frm.doc.inpatient_occupancies[inpatient_occupancy].left != 1){ + let not_left_service_unit = null; + for (let inpatient_occupancy in frm.doc.inpatient_occupancies) { + if (frm.doc.inpatient_occupancies[inpatient_occupancy].left != 1) { not_left_service_unit = frm.doc.inpatient_occupancies[inpatient_occupancy].service_unit; } } From 3a6e6af84f0d48b67fcca92f4ae91c03d6fb4b6a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 10:02:31 +0530 Subject: [PATCH 245/410] fix: add title to validation dialog --- .../healthcare/doctype/inpatient_record/inpatient_record.py | 4 ++-- .../healthcare/doctype/patient_encounter/patient_encounter.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 802ab414c0c..cf63b65f4d6 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -29,7 +29,7 @@ class InpatientRecord(Document): if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): frappe.throw(_('Expected and Discharge dates cannot be less than Admission Schedule date')) - + def validate_already_scheduled_or_admitted(self): query = """ select name, status @@ -168,7 +168,7 @@ def validate_invoiced_inpatient(inpatient_record): if pending_invoices: frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", " - .join(pending_invoices))) + .join(pending_invoices)), title=_('Unbilled Invoices')) def get_pending_doc(doc, doc_name_list, pending_invoices): if doc_name_list: diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index ef1068e6cb5..edcee99d4bc 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -215,7 +215,7 @@ var schedule_inpatient = function(frm) { args: args }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, From 82f639da5980c0aa6337e545b6be5d3899641843 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 11:35:00 +0530 Subject: [PATCH 246/410] fix: Employee Advance Return not working (#21812) --- erpnext/hr/doctype/employee_advance/employee_advance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 23e4992066b..db39eff0e4d 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -146,7 +146,7 @@ def create_return_through_additional_salary(doc): return additional_salary @frappe.whitelist() -def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account): +def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, mode_of_payment=None): return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) mode_of_payment_type = '' From 1a6dcd7f45a27925d82ac3681443ebdab8b2f023 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 12:07:43 +0530 Subject: [PATCH 247/410] fix: plc conversion rate set infinitely (#21820) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 44da9cae355..898955e51b2 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -265,7 +265,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ plc_conversion_rate: function(doc) { if (!this.in_apply_price_list) { - this.apply_price_list(); + this.apply_price_list(null, true); } }, From 7afee887a841ed3a1a9b733d7363e5e0f88ad3ae Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 13:11:48 +0530 Subject: [PATCH 248/410] fix: Fetch customer into Delivery Note from Pick List --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 231af1a0227..1f8d009f925 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -300,6 +300,7 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) delivery_note.pick_list = pick_list.name + delivery_note.customer = pick_list.customer if pick_list.customer else None return delivery_note From 90b4452a254627ca82573dafab5342d1c73e2aae Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 14:03:29 +0530 Subject: [PATCH 249/410] fix: Supplier Invoice No not fetched in Import Supplier Invoice --- .../import_supplier_invoice/import_supplier_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 6784ea8a5bc..6b9567c0e57 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -58,7 +58,7 @@ class ImportSupplierInvoice(Document): "naming_series": self.invoice_series, "document_type": line.TipoDocumento.text, "bill_date": get_datetime_str(line.Data.text), - "invoice_no": line.Numero.text, + "bill_no": line.Numero.text, "total_discount": 0, "items": [], "buying_price_list": self.default_buying_price_list @@ -249,7 +249,7 @@ def create_supplier(supplier_group, args): return existing_supplier_name else: - + new_supplier = frappe.new_doc("Supplier") new_supplier.supplier_name = re.sub('&', '&', args.supplier) new_supplier.supplier_group = supplier_group From feaf5c7edb0b712574b48d3e314058668562642c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 21 May 2020 14:08:25 +0530 Subject: [PATCH 250/410] fix: dict object has no attribute append --- .../exponential_smoothing_forecasting.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index b5127f133ca..1b49df6af87 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -45,7 +45,7 @@ class ForecastingReport(ExponentialSmoothingForecast): def execute_report(self): self.prepare_periodical_data() self.forecast_future_data() - self.data = self.period_wise_data.values() + self.prepare_final_data() self.add_total() columns = self.get_columns() @@ -108,7 +108,17 @@ class ForecastingReport(ExponentialSmoothingForecast): """.format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond), tuple(input_data), as_dict=1) + def prepare_final_data(self): + self.data = [] + + if not self.period_wise_data: return + + for key in self.period_wise_data: + self.data.append(self.period_wise_data.get(key)) + def add_total(self): + if not self.data: return + total_row = { "item_code": _(frappe.bold("Total Quantity")) } From 5cef8db4db7b7ddf4d2f789cd10dcab13ce18987 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 21 May 2020 14:32:24 +0530 Subject: [PATCH 251/410] fix(patch): Handle single value in patch (#21823) --- .../patches/v12_0/remove_duplicate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 98a2fcf27ea..6353304d7aa 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -40,5 +40,5 @@ def get_duplicate_records(): def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" - if duplicate_records_list: - frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file + if not duplicate_records_list: return + frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file From 7978907cc80ee106d0fbf95126f7b93ebdf85d4b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 21 May 2020 18:10:13 +0530 Subject: [PATCH 252/410] feat: BOM template (#21262) Co-authored-by: Marica --- erpnext/controllers/queries.py | 9 +- erpnext/manufacturing/doctype/bom/bom.js | 189 +++++- erpnext/manufacturing/doctype/bom/bom.json | 14 +- erpnext/manufacturing/doctype/bom/bom.py | 215 ++++-- erpnext/manufacturing/doctype/bom/bom_list.js | 6 +- .../doctype/bom_item/bom_item.json | 17 +- .../doctype/work_order/work_order.js | 26 + .../doctype/work_order/work_order.py | 50 +- .../work_order_item/work_order_item.json | 632 ++++-------------- erpnext/stock/doctype/item/item.py | 11 + 10 files changed, 546 insertions(+), 623 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5fbc460a973..f6a8d27d440 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -188,12 +188,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals # scan description only if items are less than 50000 description_cond = 'or tabItem.description LIKE %(txt)s' - extra_cond = " and tabItem.has_variants=0" - if (filters and isinstance(filters, dict) - and filters.get("doctype") == "BOM"): - extra_cond = "" - del filters["doctype"] - return frappe.db.sql("""select tabItem.name, if(length(tabItem.item_name) > 40, concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, @@ -204,10 +198,10 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals from tabItem where tabItem.docstatus < 2 and tabItem.disabled=0 + and tabItem.has_variants=0 and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s) {description_cond}) - {extra_cond} {fcond} {mcond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), @@ -218,7 +212,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals key=searchfield, columns=columns, scond=searchfields, - extra_cond=extra_cond, fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'), description_cond = description_cond), diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 898955e51b2..47b42072412 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -29,10 +29,7 @@ frappe.ui.form.on("BOM", { frm.set_query("item", function() { return { - query: "erpnext.controllers.queries.item_query", - filters: { - "doctype": "BOM" - } + query: "erpnext.manufacturing.doctype.bom.bom.item_query" }; }); @@ -44,9 +41,12 @@ frappe.ui.form.on("BOM", { }; }); - frm.set_query("item_code", "items", function() { + frm.set_query("item_code", "items", function(doc) { return { - query: "erpnext.controllers.queries.item_query" + query: "erpnext.manufacturing.doctype.bom.bom.item_query", + filters: { + "item_code": doc.item + } }; }); @@ -96,6 +96,12 @@ frappe.ui.form.on("BOM", { frm.trigger("make_work_order"); }, __("Create")); + if (frm.doc.has_variants) { + frm.add_custom_button(__("Variant BOM"), function() { + frm.trigger("make_variant_bom"); + }, __("Create")); + } + if (frm.doc.inspection_required) { frm.add_custom_button(__("Quality Inspection"), function() { frm.trigger("make_quality_inspection"); @@ -124,7 +130,7 @@ frappe.ui.form.on("BOM", { } - if (frm.doc.__onload && frm.doc.__onload["has_variants"]) { + if (frm.doc.has_variants) { frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}', [ `variants`, @@ -138,9 +144,52 @@ frappe.ui.form.on("BOM", { }, make_work_order: function(frm) { + frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items + }, + freeze: true, + callback: function(r) { + if(r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }); + }, + + make_variant_bom: function(frm) { + frm.events.setup_variant_prompt(frm, "Variant BOM", (frm, item, data, variant_items) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.bom.bom.make_variant_bom", + args: { + source_name: frm.doc.name, + bom_no: frm.doc.name, + item: item, + variant_items: variant_items + }, + freeze: true, + callback: function(r) { + if(r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }, true); + }, + + setup_variant_prompt: function(frm, title, callback, skip_qty_field) { const fields = []; - if (frm.doc.__onload && frm.doc.__onload["has_variants"]) { + if (frm.doc.has_variants) { fields.push({ fieldtype: 'Link', label: __('Variant Item'), @@ -158,34 +207,106 @@ frappe.ui.form.on("BOM", { }); } - fields.push({ - fieldtype: 'Float', - label: __('Qty To Manufacture'), - fieldname: 'qty', - reqd: 1, - default: 1 + if (!skip_qty_field) { + fields.push({ + fieldtype: 'Float', + label: __('Qty To Manufacture'), + fieldname: 'qty', + reqd: 1, + default: 1 + }); + } + + var has_template_rm = frm.doc.items.filter(d => d.has_variants === 1) || []; + if (has_template_rm && has_template_rm.length > 0) { + fields.push({ + fieldname: "items", + fieldtype: "Table", + label: __("Raw Materials"), + fields: [ + { + fieldname: "item_code", + options: "Item", + label: __("Template Item"), + fieldtype: "Link", + in_list_view: 1, + reqd: 1, + }, + { + fieldname: "varint_item_code", + options: "Item", + label: __("Variant Item"), + fieldtype: "Link", + in_list_view: 1, + reqd: 1, + get_query: function(data) { + if (!data.item_code) { + frappe.throw(__("Select template item")); + } + + return { + query: "erpnext.controllers.queries.item_query", + filters: { + "variant_of": data.item_code + } + }; + } + }, + { + fieldname: "qty", + label: __("Quantity"), + fieldtype: "Float", + in_list_view: 1, + reqd: 1, + }, + { + fieldname: "source_warehouse", + label: __("Source Warehouse"), + fieldtype: "Link", + options: "Warehouse" + }, + { + fieldname: "operation", + label: __("Operation"), + fieldtype: "Data", + hidden: 1, + } + ], + in_place_edit: true, + data: [], + get_data: function () { + return []; + }, + }); + } + + let dialog = frappe.prompt(fields, data => { + let item = data.item || frm.doc.item; + let variant_items = data.items || []; + + variant_items.forEach(d => { + if (!d.varint_item_code) { + frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); + } + }) + + callback(frm, item, data, variant_items); + + }, __(title), __("Create")); + + has_template_rm.forEach(d => { + dialog.fields_dict.items.df.data.push({ + "item_code": d.item_code, + "varint_item_code": "", + "qty": d.qty, + "source_warehouse": d.source_warehouse, + "operation": d.operation + }); }); - frappe.prompt(fields, data => { - let item = data.item || frm.doc.item; - - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project - }, - freeze: true, - callback: function(r) { - if(r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - } - }); - }, __("Enter Value"), __("Create")); + if (has_template_rm) { + dialog.fields_dict.items.grid.refresh(); + } }, make_quality_inspection: function(frm) { diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 4ce0ecf3f27..f551b91597f 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -53,6 +53,7 @@ "section_break_25", "description", "column_break_27", + "has_variants", "section_break0", "exploded_items", "website_section", @@ -498,6 +499,17 @@ "options": "Currency", "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "fetch_from": "item.has_variants", + "fieldname": "has_variants", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Has Variants", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-sitemap", @@ -505,7 +517,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2020-05-05 14:29:32.634952", + "modified": "2020-05-21 12:29:32.634952", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6ac653e37a4..1bdac5731ef 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -3,13 +3,16 @@ from __future__ import unicode_literals import frappe, erpnext -from frappe.utils import cint, cstr, flt +from frappe.utils import cint, cstr, flt, today from frappe import _ from erpnext.setup.utils import get_exchange_rate from frappe.website.website_generator import WebsiteGenerator from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_price_list_rate from frappe.core.doctype.version.version import get_diff +from erpnext.controllers.queries import get_match_cond +from erpnext.stock.doctype.item.item import get_item_details +from frappe.model.mapper import get_mapped_doc import functools @@ -59,11 +62,6 @@ class BOM(WebsiteGenerator): self.name = name - def onload(self): - super(BOM, self).onload() - if self.get("item") and cint(frappe.db.get_value("Item", self.item, "has_variants")): - self.set_onload("has_variants", True) - def validate(self): self.route = frappe.scrub(self.name).replace('_', '-') self.clear_operations() @@ -103,9 +101,7 @@ class BOM(WebsiteGenerator): self.manage_default_bom() def get_item_det(self, item_code): - item = frappe.db.sql("""select name, item_name, docstatus, description, image, - is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, include_item_in_manufacturing - from `tabItem` where name=%s""", item_code, as_dict = 1) + item = get_item_details(item_code) if not item: frappe.throw(_("Item: {0} does not exist in the system").format(item_code)) @@ -150,10 +146,10 @@ class BOM(WebsiteGenerator): item = self.get_item_det(args['item_code']) - args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or '' + args['bom_no'] = args['bom_no'] or item and cstr(item['default_bom']) or '' args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or - item and item[0].include_item_in_manufacturing or 0) - args.update(item[0]) + item and item.include_item_in_manufacturing or 0) + args.update(item) rate = self.get_rm_rate(args) ret_item = { @@ -185,40 +181,14 @@ class BOM(WebsiteGenerator): self.rm_cost_as_per = "Valuation Rate" if arg.get('scrap_items'): - rate = self.get_valuation_rate(arg) + rate = get_valuation_rate(arg) elif arg: #Customer Provided parts will have zero rate if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'): if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom: rate = flt(self.get_bom_unitcost(arg['bom_no'])) * (arg.get("conversion_factor") or 1) else: - if self.rm_cost_as_per == 'Valuation Rate': - rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1) - elif self.rm_cost_as_per == 'Last Purchase Rate': - rate = flt(arg.get('last_purchase_rate') \ - or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \ - * (arg.get("conversion_factor") or 1) - elif self.rm_cost_as_per == "Price List": - if not self.buying_price_list: - frappe.throw(_("Please select Price List")) - args = frappe._dict({ - "doctype": "BOM", - "price_list": self.buying_price_list, - "qty": arg.get("qty") or 1, - "uom": arg.get("uom") or arg.get("stock_uom"), - "stock_uom": arg.get("stock_uom"), - "transaction_type": "buying", - "company": self.company, - "currency": self.currency, - "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function - "conversion_factor": arg.get("conversion_factor") or 1, - "plc_conversion_rate": 1, - "ignore_party": True - }) - item_doc = frappe.get_doc("Item", arg.get("item_code")) - out = frappe._dict() - get_price_list_rate(args, item_doc, out) - rate = out.price_list_rate + rate = get_bom_item_rate(arg, self) if not rate: if self.rm_cost_as_per == "Price List": @@ -286,31 +256,6 @@ class BOM(WebsiteGenerator): where is_active = 1 and name = %s""", bom_no, as_dict=1) return bom and bom[0]['unit_cost'] or 0 - def get_valuation_rate(self, args): - """ Get weighted average of valuation rate from all warehouses """ - - total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0 - for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin` - where item_code=%s""", args['item_code'], as_dict=1): - total_qty += flt(d.actual_qty) - total_value += flt(d.stock_value) - - if total_qty: - valuation_rate = total_value / total_qty - - if valuation_rate <= 0: - last_valuation_rate = frappe.db.sql("""select valuation_rate - from `tabStock Ledger Entry` - where item_code = %s and valuation_rate > 0 - order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) - - valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 - - if not valuation_rate: - valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate") - - return flt(valuation_rate) - def manage_default_bom(self): """ Uncheck others if current one is selected as default or check the current one as default if it the only bom for the selected item, @@ -624,6 +569,62 @@ class BOM(WebsiteGenerator): if not d.batch_size or d.batch_size <= 0: d.batch_size = 1 +def get_bom_item_rate(args, bom_doc): + if bom_doc.rm_cost_as_per == 'Valuation Rate': + rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1) + elif bom_doc.rm_cost_as_per == 'Last Purchase Rate': + rate = ( flt(args.get('last_purchase_rate')) \ + or frappe.db.get_value("Item", args['item_code'], "last_purchase_rate")) \ + * (args.get("conversion_factor") or 1) + elif bom_doc.rm_cost_as_per == "Price List": + if not bom_doc.buying_price_list: + frappe.throw(_("Please select Price List")) + bom_args = frappe._dict({ + "doctype": "BOM", + "price_list": bom_doc.buying_price_list, + "qty": args.get("qty") or 1, + "uom": args.get("uom") or args.get("stock_uom"), + "stock_uom": args.get("stock_uom"), + "transaction_type": "buying", + "company": bom_doc.company, + "currency": bom_doc.currency, + "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function + "conversion_factor": args.get("conversion_factor") or 1, + "plc_conversion_rate": 1, + "ignore_party": True + }) + item_doc = frappe.get_cached_doc("Item", args.get("item_code")) + out = frappe._dict() + get_price_list_rate(bom_args, item_doc, out) + rate = out.price_list_rate + + return rate + +def get_valuation_rate(args): + """ Get weighted average of valuation rate from all warehouses """ + + total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0 + for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin` + where item_code=%s""", args['item_code'], as_dict=1): + total_qty += flt(d.actual_qty) + total_value += flt(d.stock_value) + + if total_qty: + valuation_rate = total_value / total_qty + + if valuation_rate <= 0: + last_valuation_rate = frappe.db.sql("""select valuation_rate + from `tabStock Ledger Entry` + where item_code = %s and valuation_rate > 0 + order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) + + valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 + + if not valuation_rate: + valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate") + + return flt(valuation_rate) + def get_list_context(context): context.title = _("Bill of Materials") # context.introduction = _('Boms') @@ -639,6 +640,8 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty, item.image, bom.project, + bom_item.rate, + bom_item.amount, item.stock_uom, item.item_group, item.allow_alternative_item, @@ -655,6 +658,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite where bom_item.docstatus < 2 and bom.name = %(bom)s + and ifnull(item.has_variants, 0) = 0 and item.is_stock_item in (1, {is_stock_item}) {where_conditions} group by item_code, stock_uom @@ -897,3 +901,84 @@ def get_bom_diff(bom1, bom2): out.removed.append([df.fieldname, d.as_dict()]) return out + +def item_query(doctype, txt, searchfield, start, page_len, filters): + meta = frappe.get_meta("Item", cached=True) + searchfields = meta.get_search_fields() + + order_by = "idx desc, name, item_name" + + fields = ["name", "item_group", "item_name", "description"] + fields.extend([field for field in searchfields + if not field in ["name", "item_group", "description"]]) + + searchfields = searchfields + [field for field in [searchfield or "name", "item_code", "item_group", "item_name"] + if not field in searchfields] + + query_filters = { + "disabled": 0, + "ifnull(end_of_life, '5050-50-50')": (">", today()) + } + + or_cond_filters = {} + if txt: + for s_field in searchfields: + or_cond_filters[s_field] = ("like", "%{0}%".format(txt)) + + barcodes = frappe.get_all("Item Barcode", + fields=["distinct parent as item_code"], + filters = {"barcode": ("like", "%{0}%".format(txt))}) + + barcodes = [d.item_code for d in barcodes] + if barcodes: + or_cond_filters["name"] = ("in", barcodes) + + for cond in get_match_cond(doctype, as_condition=False): + for key, value in cond.items(): + if key == doctype: + key = "name" + + query_filters[key] = ("in", value) + + if filters and filters.get("item_code"): + has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants") + if not has_variants: + query_filters["has_variants"] = 0 + + return frappe.get_all("Item", + fields = fields, filters=query_filters, + or_filters = or_cond_filters, order_by=order_by, + limit_start=start, limit_page_length=page_len, as_list=1) + +@frappe.whitelist() +def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None): + from erpnext.manufacturing.doctype.work_order.work_order import add_variant_item + + def postprocess(source, doc): + doc.item = item + doc.quantity = 1 + + item_data = get_item_details(item) + doc.update({ + "item_name": item_data.item_name, + "description": item_data.description, + "uom": item_data.stock_uom, + "allow_alternative_item": item_data.allow_alternative_item + }) + + add_variant_item(variant_items, doc, source_name) + + doc = get_mapped_doc('BOM', source_name, { + 'BOM': { + 'doctype': 'BOM', + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'BOM Item': { + 'doctype': 'BOM Item', + 'condition': lambda doc: doc.has_variants == 0 + }, + }, target_doc, postprocess) + + return doc \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 2b06ed72eda..94cb466bd8a 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -1,7 +1,9 @@ frappe.listview_settings['BOM'] = { - add_fields: ["is_active", "is_default", "total_cost"], + add_fields: ["is_active", "is_default", "total_cost", "has_variants"], get_indicator: function(doc) { - if(doc.is_default) { + if(doc.is_active && doc.has_variants) { + return [__("Template"), "orange", "has_variants,=,Yes"]; + } else if(doc.is_default) { return [__("Default"), "green", "is_default,=,Yes"]; } else if(doc.is_active) { return [__("Active"), "blue", "is_active,=,Yes"]; diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index f094be4c647..e34be61bc75 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-02-22 01:27:49", "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "item_code", "item_name", @@ -33,6 +35,7 @@ "scrap", "qty_consumed_per_unit", "section_break_27", + "has_variants", "include_item_in_manufacturing", "original_item" ], @@ -57,6 +60,7 @@ "label": "Item Name" }, { + "depends_on": "eval:parent.with_operations == 1", "fieldname": "operation", "fieldtype": "Link", "label": "Item operation", @@ -258,11 +262,22 @@ "label": "Original Item", "options": "Item", "read_only": 1 + }, + { + "default": "0", + "fetch_from": "item_code.has_variants", + "fieldname": "has_variants", + "fieldtype": "Check", + "label": "Has Variants", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-11-22 11:38:52.087303", + "links": [], + "modified": "2020-04-09 14:30:26.535546", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index c1255719606..a244f582c42 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -449,6 +449,32 @@ frappe.ui.form.on("Work Order Item", { } }); } + }, + + item_code: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.item_code) { + frappe.call({ + method: "erpnext.stock.doctype.item.item.get_item_details", + args: { + item_code: row.item_code, + company: frm.doc.company + }, + callback: function(r) { + if (r.message) { + frappe.model.set_value(cdt, cdn, { + "required_qty": 1, + "item_name": r.message.item_name, + "description": r.message.description, + "source_warehouse": r.message.default_warehouse, + "allow_alternative_item": r.message.allow_alternative_item, + "include_item_in_manufacturing": r.message.include_item_in_manufacturing + }); + } + } + }); + } } }); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c2789559b07..e2233a3e2f6 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -8,9 +8,9 @@ import math from frappe import _ from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours from frappe.model.document import Document -from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict +from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict, get_bom_item_rate from dateutil.relativedelta import relativedelta -from erpnext.stock.doctype.item.item import validate_end_of_life +from erpnext.stock.doctype.item.item import validate_end_of_life, get_item_defaults from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError from erpnext.projects.doctype.timesheet.timesheet import OverlapError from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations @@ -541,6 +541,8 @@ class WorkOrder(Document): # For instance in BOM Explosion Item child table, the items coming from sub assembly items for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999): self.append('required_items', { + 'rate': item.rate, + 'amount': item.amount, 'operation': item.operation, 'item_code': item.item_code, 'item_name': item.item_name, @@ -637,9 +639,10 @@ def get_bom_operations(doctype, txt, searchfield, start, page_len, filters): filters = filters, fields = ['operation'], as_list=1) @frappe.whitelist() -def get_item_details(item, project = None): +def get_item_details(item, project = None, skip_bom_info=False): res = frappe.db.sql(""" - select stock_uom, description + select stock_uom, description, item_name, allow_alternative_item, + include_item_in_manufacturing from `tabItem` where disabled=0 and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s) @@ -650,6 +653,7 @@ def get_item_details(item, project = None): return {} res = res[0] + if skip_bom_info: return res filters = {"item": item, "is_default": 1} @@ -681,7 +685,7 @@ def get_item_details(item, project = None): return res @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -696,8 +700,44 @@ def make_work_order(bom_no, item, qty=0, project=None): wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() + if variant_items: + add_variant_item(variant_items, wo_doc, bom_no, "required_items") + return wo_doc +def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): + if isinstance(variant_items, string_types): + variant_items = json.loads(variant_items) + + for item in variant_items: + args = frappe._dict({ + "item_code": item.get("varint_item_code"), + "required_qty": item.get("qty"), + "qty": item.get("qty"), # for bom + "source_warehouse": item.get("source_warehouse"), + "operation": item.get("operation") + }) + + bom_doc = frappe.get_cached_doc("BOM", bom_no) + item_data = get_item_details(args.item_code, skip_bom_info=True) + args.update(item_data) + + args["rate"] = get_bom_item_rate({ + "item_code": args.get("item_code"), + "qty": args.get("required_qty"), + "uom": args.get("stock_uom"), + "stock_uom": args.get("stock_uom"), + "conversion_factor": 1 + }, bom_doc) + + if not args.source_warehouse: + args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"), + wo_doc.company).default_warehouse + + args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) + args["uom"] = item_data.stock_uom + wo_doc.append(table_name, args) + @frappe.whitelist() def check_if_scrap_warehouse_mandatory(bom_no): res = {"set_scrap_wh_mandatory": False } diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index 44421626361..3acf5727d19 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -1,526 +1,144 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-04-18 07:38:26.314642", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-04-18 07:38:26.314642", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "operation", + "item_code", + "source_warehouse", + "column_break_3", + "item_name", + "description", + "allow_alternative_item", + "include_item_in_manufacturing", + "qty_section", + "required_qty", + "rate", + "amount", + "column_break_11", + "transferred_qty", + "consumed_qty", + "available_qty_at_source_warehouse", + "available_qty_at_wip_warehouse" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "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": "Operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "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": "operation", + "fieldtype": "Link", + "label": "Operation", + "options": "Operation" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "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": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Source Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "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": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty_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, - "label": "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": "qty_section", + "fieldtype": "Section Break", + "label": "Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Required Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!parent.skip_transfer", - "fieldname": "transferred_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": "Transferred Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!parent.skip_transfer", + "fieldname": "transferred_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Transferred Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "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 - }, + "default": "0", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "label": "Allow Alternative Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "include_item_in_manufacturing", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Item In Manufacturing", - "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 - }, + "default": "0", + "fieldname": "include_item_in_manufacturing", + "fieldtype": "Check", + "label": "Include Item In Manufacturing" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "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_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!parent.skip_transfer", - "fieldname": "consumed_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": "Consumed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!parent.skip_transfer", + "fieldname": "consumed_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Consumed Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "available_qty_at_source_warehouse", - "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": "Available Qty at Source Warehouse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "available_qty_at_source_warehouse", + "fieldtype": "Float", + "label": "Available Qty at Source Warehouse", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "available_qty_at_wip_warehouse", - "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": "Available Qty at WIP Warehouse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "available_qty_at_wip_warehouse", + "fieldtype": "Float", + "label": "Available Qty at WIP Warehouse", + "read_only": 1 + }, + { + "fieldname": "rate", + "fieldtype": "Currency", + "label": "Rate", + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-20 19:04:38.508839", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Work Order Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-04-13 18:46:32.966416", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Work Order Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 7a1c1279eae..3436a5d0136 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1143,6 +1143,17 @@ def set_item_default(item_code, company, fieldname, value): d.db_insert() item.clear_cache() +@frappe.whitelist() +def get_item_details(item_code, company=None): + out = frappe._dict() + if company: + out = get_item_defaults(item_code, company) or frappe._dict() + + doc = frappe.get_cached_doc("Item", item_code) + out.update(doc.as_dict()) + + return out + @frappe.whitelist() def get_uom_conv_factor(uom, stock_uom): uoms = [uom, stock_uom] From ce8f8f4ed7909decaa01fa61833f69d260925576 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Thu, 21 May 2020 18:35:03 +0530 Subject: [PATCH 253/410] fix: hide delete company transacations button if not system manager (#21839) --- erpnext/setup/doctype/company/company.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 0fbe49eab76..875904fe6fe 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -107,6 +107,9 @@ frappe.ui.form.on("Company", { erpnext.company.set_chart_of_accounts_options(frm.doc); + if (!frappe.user.has_role('System Manager')) { + frm.get_field("delete_company_transactions").hide(); + } }, make_default_tax_template: function(frm) { @@ -134,7 +137,7 @@ frappe.ui.form.on("Company", { var d = frappe.prompt({ fieldtype:"Data", fieldname: "company_name", - label: __("Please re-type company name to confirm"), + label: __("Please enter the company name to confirm"), reqd: 1, description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.") }, From 82523a5487c17382c1fe854ea041e9ca21aacdc6 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 21 May 2020 18:42:10 +0530 Subject: [PATCH 254/410] fix(set_serial_no_status): auto commit on many writes (#21845) --- erpnext/patches/v12_0/set_serial_no_status.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index 4ec84ef0f9e..abba37d48ef 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,8 +5,12 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` - where (status is NULL OR status='')""", as_dict = 1): + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1) + if len(serial_no_list) > 20000: + frappe.db.auto_commit_on_many_writes = True + + for serial_no in serial_no_list: if serial_no.get("delivery_document_type"): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): @@ -14,4 +18,7 @@ def execute(): else: status = "Active" - frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False From c07265ea3fcecb6ef04368fe2ece92c5163e47e4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 21 May 2020 18:47:39 +0530 Subject: [PATCH 255/410] fix: convert goals point to flt (#21844) --- .../hr/doctype/appraisal_template/appraisal_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py index e5d3c42e1bd..d0dfad4be31 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from frappe import _ from frappe.model.document import Document @@ -11,11 +11,11 @@ from frappe.model.document import Document class AppraisalTemplate(Document): def validate(self): self.check_total_points() - - def check_total_points(self): + + def check_total_points(self): total_points = 0 for d in self.get("goals"): - total_points += int(d.per_weightage or 0) + total_points += flt(d.per_weightage) if cint(total_points) != 100: frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) From d7d4bed0b13f8a84ccf2d34eab1fa4b6351b1ae6 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 22 May 2020 01:10:55 +0530 Subject: [PATCH 256/410] adding report card in education desk --- erpnext/education/desk_page/education/education.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index fc2697f0d75..b341ec4b99b 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -64,6 +64,11 @@ "hidden": 0, "label": "Assessment Reports", "links": "[\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Course wise Assessment Report\",\n \"name\": \"Course wise Assessment Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Final Assessment Grades\",\n \"name\": \"Final Assessment Grades\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Plan\"\n ],\n \"doctype\": \"Assessment Plan\",\n \"is_query_report\": true,\n \"label\": \"Assessment Plan Status\",\n \"name\": \"Assessment Plan Status\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Student Report Generation Tool\",\n \"name\": \"Student Report Generation Tool\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Student Fee Collection\",\n \"name\": \"Student Fee Collection\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Monthly Attendance Sheet\",\n \"name\": \"Student Monthly Attendance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Absent Student Report\",\n \"name\": \"Absent Student Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Program Enrollment\"\n ],\n \"doctype\": \"Program Enrollment\",\n \"is_query_report\": true,\n \"label\": \"Student and Guardian Contact Details\",\n \"name\": \"Student and Guardian Contact Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Batch-Wise Attendance\",\n \"name\": \"Student Batch-Wise Attendance\",\n \"type\": \"report\"\n }\n]" } ], "category": "Domains", @@ -77,7 +82,7 @@ "idx": 0, "is_standard": 1, "label": "Education", - "modified": "2020-04-01 11:28:51.011309", + "modified": "2020-05-22 01:09:13.058482", "modified_by": "Administrator", "module": "Education", "name": "Education", From dd3e52184c5e41fba7e0e8337e580118d46de31f Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 22 May 2020 10:48:35 +0530 Subject: [PATCH 257/410] fix: Added Inactive serial no status (#21848) --- erpnext/patches.txt | 2 +- erpnext/patches/v12_0/set_serial_no_status.py | 4 +++- erpnext/stock/doctype/serial_no/serial_no.json | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 2 ++ erpnext/stock/doctype/serial_no/serial_no_list.js | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index af7cb8eeda9..2435b1c5174 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -684,7 +684,7 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation -erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index abba37d48ef..3b5f5ef3407 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,7 +5,7 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No` where (status is NULL OR status='')""", as_dict = 1) if len(serial_no_list) > 20000: frappe.db.auto_commit_on_many_writes = True @@ -15,6 +15,8 @@ def execute(): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): status = "Expired" + elif not serial_no.get("warehouse"): + status = "Inactive" else: status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 731a7302797..d9f8b627545 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -420,14 +420,14 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nDelivered\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-04-08 13:29:58.517772", + "modified": "2020-05-21 19:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 914eea379a8..f3514c7385d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -42,6 +42,8 @@ class SerialNo(StockController): self.status = "Delivered" elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): self.status = "Expired" + elif not self.warehouse: + self.status = "Inactive" else: self.status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 651f790583d..7526d1d8a5c 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Serial No'] = { return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if (!doc.warehouse) { + return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } From baef43977be81a9db11480cdbce7a3514093e108 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Fri, 22 May 2020 10:50:13 +0530 Subject: [PATCH 258/410] fix: set customer and supplier details using sql (#21846) * fix: set customer and supplier details using sql instead of slowing down the query with get_doc and save() we can just use sql to update the required values for customer and supplier Signed-off-by: Chinmay D. Pai * chore: remove extra quote Co-authored-by: Himanshu * fix: update sql query to include tabPrice List Signed-off-by: Chinmay D. Pai Co-authored-by: Himanshu --- ...er_supplier_based_on_type_of_item_price.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py index 60aec05466a..b8efb210a03 100644 --- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -1,15 +1,29 @@ from __future__ import unicode_literals import frappe + def execute(): - invalid_selling_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" - ) - invalid_buying_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" - ) - docs_to_modify = invalid_buying_item_price + invalid_selling_item_price - for d in docs_to_modify: - # saving the doc will auto reset invalid customer/supplier field - doc = frappe.get_doc("Item Price", d[0]) - doc.save() \ No newline at end of file + """ + set proper customer and supplier details for item price + based on selling and buying values + """ + + # update for selling + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL + WHERE ip.`selling` = 1 + AND ip.`buying` = 0 + AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") + + # update for buying + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL + WHERE ip.`selling` = 0 + AND ip.`buying` = 1 + AND (ip.`customer` IS NOT NULL OR ip.`customer` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") From 1763d3e7067c95678089afe17dbb268f4ec9c4df Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 22 May 2020 10:51:19 +0530 Subject: [PATCH 259/410] fix: expense account error message in DN (#21851) Changed error message if expense account not set for item in Delivery Note. Earlier: Expense or Difference account is mandatory for Item IT - 6 as it impacts overall stock value After fix: Expense Account not set for Item IT - 6. Please set an Expense Account for the item in the Items table --- 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 86de80815db..90d293088b0 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -226,7 +226,7 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) + frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) else: is_expense_account = frappe.db.get_value("Account", From 8d7f5d342df4bb0536490d115b33bbc18457a40d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 22 May 2020 12:28:45 +0530 Subject: [PATCH 260/410] fix: Item tax template not getting mapped from source to traget doc (#21862) --- erpnext/public/js/controllers/transaction.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 28c2102aef5..637d3b3267d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1652,8 +1652,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { if(item.item_code && r.message.hasOwnProperty(item.item_code)) { - item.item_tax_template = r.message[item.item_code].item_tax_template; - item.item_tax_rate = r.message[item.item_code].item_tax_rate; + if (!item.item_tax_template) { + item.item_tax_template = r.message[item.item_code].item_tax_template; + item.item_tax_rate = r.message[item.item_code].item_tax_rate; + } me.add_taxes_from_item_tax_template(item.item_tax_rate); } else { item.item_tax_template = ""; From 78a6b6b45dceee27b4fab3d1e822299d98463956 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 13:13:17 +0530 Subject: [PATCH 261/410] fix: Remove duplicate leave ledger entry (#21871) * fix: Remove duplicate leave ledger entry * fix: Remove duplicate leave ledger entry --- .../remove_duplicate_leave_ledger_entries.py | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 6353304d7aa..24286dcebf9 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -6,6 +6,7 @@ import frappe def execute(): """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry') if not frappe.db.a_row_exists("Leave Ledger Entry"): return @@ -14,31 +15,32 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" - return frappe.db.sql_list(""" - WITH duplicate_records AS - (SELECT - name, transaction_name, is_carry_forward, - ROW_NUMBER() over(partition by transaction_name order by creation)as row - FROM `tabLeave Ledger Entry` l - WHERE (EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name <> l.name - AND employee = l.employee - AND docstatus = 1 - AND leave_type = l.leave_type - AND is_carry_forward=l.is_carry_forward - AND to_date = l.to_date - AND from_date = l.from_date - AND is_expired = 1 - ))) - SELECT name FROM duplicate_records WHERE row > 1 + return frappe.db.sql(""" + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 + ORDER BY + creation """) def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" if not duplicate_records_list: return - frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file + for d in duplicate_records_list: + frappe.db.sql(''' + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + ''', tuple(d)) \ No newline at end of file From 5cf90baf165913e99de6a17ac5d7ab65066d8e96 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 22 May 2020 15:09:57 +0530 Subject: [PATCH 262/410] feat: rename loan management to loan on Desk Page --- .../desk_page/loan_management/loan_management.json | 6 +++--- erpnext/patches.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan_management/loan_management.json index f9ea978ed6e..d2a17630c9e 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan_management/loan_management.json @@ -36,11 +36,11 @@ "extends_another_page": 0, "idx": 0, "is_standard": 1, - "label": "Loan Management", - "modified": "2020-04-02 11:28:51.380509", + "label": "Loan", + "modified": "2020-05-22 11:28:51.380509", "modified_by": "Administrator", "module": "Loan Management", - "name": "Loan Management", + "name": "Loan", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2435b1c5174..eacede6a92f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -691,3 +691,4 @@ erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") +execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) From 2b277a7eec48549157275ae3495413358f038377 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 15:52:15 +0530 Subject: [PATCH 263/410] fix(patch): rerun remove_duplicate_leave_ledger_entries (#21878) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2435b1c5174..c23357466c8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -678,7 +678,7 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) From 0d147b011e5a9685de81564170f0668070d75af4 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sun, 24 May 2020 00:54:04 +0530 Subject: [PATCH 264/410] fix: set default posting_date value to None using mutable python defaults, and especially function calls, inside function definitions causes bugs that can be really hard to debug sometimes. please refrain from using such defaults. instead, using None is almost always a sane default. the values can then be manipulated inside the function instead. Signed-off-by: Chinmay D. Pai --- erpnext/accounts/deferred_revenue.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index b57e6783ceb..448011016e7 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -199,10 +199,13 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if item.get(enable_check): _book_deferred_revenue_or_expense(item) -def process_deferred_accounting(posting_date=today()): +def process_deferred_accounting(posting_date=None): ''' Converts deferred income/expense into income/expense Executed via background jobs on every month end ''' + if not posting_date: + posting_date = today() + if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')): return From 9f963a2ac730e38dc480a8ecdc523f1af168d956 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 24 May 2020 21:55:47 +0530 Subject: [PATCH 265/410] fix: Company query for number cards --- erpnext/healthcare/dashboard_fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 59da71a0ec9..967117d22c2 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -19,7 +19,7 @@ def get_company(): else: company = frappe.get_list("Company", limit=1) if company: - return company.name + return company[0].name return None def get_dashboards(): From 6ff11de2f0bb76f296d51bf8c76255743136a687 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 25 May 2020 14:54:19 +0530 Subject: [PATCH 266/410] fix: Added gram, pound to ounce conversion (#21709) * fix: pound, grams to Ounce converion factor added * fix: patch date updated for UOM Conversion Factor * fix: grams move to gram * fix: duplicate entry of uom converion factor and patch added * fix: typo fix * fix: minor changes * Update patches.txt Co-authored-by: Marica --- erpnext/patches.txt | 1 + .../patches/v12_0/update_uom_conversion_factor.py | 11 +++++++++++ .../setup_wizard/data/uom_conversion_data.json | 14 ++++++++++++++ .../setup_wizard/operations/install_fixtures.py | 15 ++++++++------- 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v12_0/update_uom_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cb82901697d..05f42bdda21 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -692,3 +692,4 @@ erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) +erpnext.patches.v12_0.update_uom_conversion_factor diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py new file mode 100644 index 00000000000..b5a20aa6fd9 --- /dev/null +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe, json + +def execute(): + from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data + + frappe.reload_doc("setup", "doctype", "UOM Conversion Factor") + frappe.reload_doc("setup", "doctype", "UOM") + frappe.reload_doc("stock", "doctype", "UOM Category") + + add_uom_data() \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/uom_conversion_data.json b/erpnext/setup/setup_wizard/data/uom_conversion_data.json index 174ecd5903e..27a917d9db8 100644 --- a/erpnext/setup/setup_wizard/data/uom_conversion_data.json +++ b/erpnext/setup/setup_wizard/data/uom_conversion_data.json @@ -1571,5 +1571,19 @@ "to_uom": "Parts Per Million", "abbr": "ppm", "value": "10000" + }, + { + "category": "Mass", + "from_uom": "Pound", + "to_uom": "Ounce", + "abbr": "oz", + "value": "16" + }, + { + "category": "Mass", + "from_uom": "Gram", + "to_uom": "Ounce", + "abbr": "oz", + "value": "0.035274" } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 8bb0a0529d7..0d70d91f739 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -336,13 +336,14 @@ def add_uom_data(): "category_name": _(d.get("category")) }).insert(ignore_permissions=True) - uom_conversion = frappe.get_doc({ - "doctype": "UOM Conversion Factor", - "category": _(d.get("category")), - "from_uom": _(d.get("from_uom")), - "to_uom": _(d.get("to_uom")), - "value": d.get("value") - }).insert(ignore_permissions=True) + if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}): + uom_conversion = frappe.get_doc({ + "doctype": "UOM Conversion Factor", + "category": _(d.get("category")), + "from_uom": _(d.get("from_uom")), + "to_uom": _(d.get("to_uom")), + "value": d.get("value") + }).insert(ignore_permissions=True) def add_market_segments(): records = [ From e5395c3ae8bf33f35139b321aedf57e7e1bbff20 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 25 May 2020 15:25:21 +0530 Subject: [PATCH 267/410] fix: filter batches based on item and warehouse in Pick List (#21780) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index d46b98b461b..3a5ef769805 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', { }; }); frm.set_query('item_code', 'locations', () => { + return erpnext.queries.item({ "is_stock_item": 1 }); + }); + frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + const row = locals[cdt][cdn]; return { + query: 'erpnext.controllers.queries.get_batch_no', filters: { - is_stock_item: 1 - } + item_code: row.item_code, + warehouse: row.warehouse + }, }; }); }, From 07f305a1d69df116efcb610405a28723639db2eb Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 16:30:59 +0530 Subject: [PATCH 268/410] refactor: open link in new tab (#21910) --- erpnext/buying/doctype/buying_settings/buying_settings.js | 2 +- erpnext/stock/doctype/stock_settings/stock_settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index a27950a9414..01b40cd26fe 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -11,7 +11,7 @@ frappe.tour['Buying Settings'] = [ { fieldname: "supp_master_name", title: "Supplier Naming By", - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "buying_price_list", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 6f9757274d1..877d0c3bbf4 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -36,7 +36,7 @@ frappe.tour['Stock Settings'] = [ { fieldname: "valuation_method", title: __("Valuation Method"), - description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") + description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") }, { fieldname: "show_barcode_field", From 1392489b609f2843115d8a6be3fcabaef6845f52 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 18:26:01 +0530 Subject: [PATCH 269/410] fix: Item Price and Add to Cart not showing on Website (#21903) * fix: Item Price and Add to Cart not showing on Website * fix: Use None as default argument --- erpnext/shopping_cart/cart.py | 19 ++++++++++++------- erpnext/shopping_cart/product_info.py | 8 +++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 4ac546e82c6..d04c8c25a34 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -319,7 +319,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +334,28 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') + if quotation and quotation.get("party_name"): + selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + party_name = quotation.get("party_name") if quotation else get_party().get("name") - quotation.selling_price_list = selling_price_list + if not selling_price_list and party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + if quotation: + quotation.selling_price_list = selling_price_list + + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125b..7c08f5b5b24 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty From 8a1cf454739b31c94bb8d4102237c3b0abf052a2 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 18:39:19 +0530 Subject: [PATCH 270/410] fix: Validation for dates (#21886) --- erpnext/hr/doctype/upload_attendance/upload_attendance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 61faea18713..edf05e827b9 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -22,6 +22,9 @@ def get_template(): args = frappe.local.form_dict + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + w = UnicodeWriter() w = add_header(w) From 316d136acabba37e58c5987f22570bc1ebf9b64b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 18:41:47 +0530 Subject: [PATCH 271/410] fix(Healthcare): unhide company field in Sample Collection, add field in Rehab DocTypes (#21907) * fix: unhide company field in Sample Collection * fix: add and set company field in rehab doctypes --- .../doctype/patient_assessment/patient_assessment.json | 10 +++++++++- .../doctype/sample_collection/sample_collection.json | 8 +++----- .../healthcare/doctype/therapy_plan/therapy_plan.json | 10 +++++++++- erpnext/patches.txt | 2 +- .../v13_0/set_company_field_in_healthcare_doctypes.py | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json index 3952a8153f7..15c94344e9b 100644 --- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json +++ b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json @@ -11,6 +11,7 @@ "patient", "assessment_template", "column_break_4", + "company", "healthcare_practitioner", "assessment_datetime", "assessment_description", @@ -127,11 +128,18 @@ "fieldname": "assessment_description", "fieldtype": "Small Text", "label": "Assessment Description" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 13:23:09.815007", + "modified": "2020-05-25 14:38:38.302399", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Assessment", diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index c352287faf4..016cfbc3ae2 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -85,11 +85,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "fieldname": "section_break_6", @@ -167,7 +165,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-04 19:17:02.707203", + "modified": "2020-05-25 14:36:46.990469", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json index ca78b6618ec..9edfeb2faa1 100644 --- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json +++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json @@ -10,6 +10,7 @@ "patient", "patient_name", "column_break_4", + "company", "status", "start_date", "section_break_3", @@ -98,10 +99,17 @@ "label": "Status", "options": "Not Started\nIn Progress\nCompleted\nCancelled", "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } ], "links": [], - "modified": "2020-04-21 13:13:43.956014", + "modified": "2020-05-25 14:38:53.649315", "modified_by": "Administrator", "module": "Healthcare", "name": "Therapy Plan", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 05f42bdda21..929f8d6b98b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,7 +688,7 @@ erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo -erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes +erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25 erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index 9d0dae45535..a7d4c665a1e 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -3,8 +3,8 @@ import frappe def execute(): company = frappe.db.get_single_value('Global Defaults', 'default_company') - doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] + doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Sample Collection' 'Patient Appointment', 'Patient Encounter', 'Vital Signs', 'Therapy Session', 'Therapy Plan', 'Patient Assessment'] for entry in doctypes: if frappe.db.exists('DocType', entry): - frappe.reload_doc("Healthcare", "doctype", entry) + frappe.reload_doc('Healthcare', 'doctype', entry) frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 4dd6b9986fbf5320a80288b871d32ded4c4e39f1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 18:42:01 +0530 Subject: [PATCH 272/410] fix(healthcare): patient vitals undefined (#21906) --- erpnext/healthcare/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index f092578003e..9abaa0784a7 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -512,10 +512,10 @@ def get_children(doctype, parent, company, is_root=False): def get_patient_vitals(patient, from_date=None, to_date=None): if not patient: return - vitals = frappe.db.get_all('Vital Signs', { + vitals = frappe.db.get_all('Vital Signs', filters={ 'docstatus': 1, 'patient': patient - }, order_by='signs_date, signs_time') + }, order_by='signs_date, signs_time', fields=['*']) if len(vitals): return vitals From 48e4abf43f34ef04ce5f06a0a423c6c640d66865 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 25 May 2020 19:21:08 +0530 Subject: [PATCH 273/410] fix: fetch depreciation amount only if depr entry is made (#21893) --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index d7efbad240d..80bccafd28e 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -111,7 +111,7 @@ def get_assets(filters): 0 end), 0) as depreciation_amount_during_the_period from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union SELECT a.asset_category, From 0a147d5c766a69ee98543fd12f1add57c8792e44 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 25 May 2020 19:21:48 +0530 Subject: [PATCH 274/410] fix: adding dashboard in course and assessment plan (#21889) * Adding dashboards * adding dashboard in course and assessment plan --- .../assessment_plan_dashboard.py | 17 +++++++++++++ .../doctype/course/course_dashboard.py | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py create mode 100644 erpnext/education/doctype/course/course_dashboard.py diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py new file mode 100644 index 00000000000..c36dfb11b54 --- /dev/null +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_plan', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py new file mode 100644 index 00000000000..752af29a9db --- /dev/null +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Course'), + 'items': ['Course Enrollment', 'Course Schedule'] + }, + { + 'label': _('Student'), + 'items': ['Student Group'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan'] + }, + ] + } \ No newline at end of file From a7f42f72565ef1e47629101cabb0ad5e57c62d71 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 19:23:17 +0530 Subject: [PATCH 275/410] fix: showing wrong record (#21884) --- erpnext/hr/desk_page/hr/hr.json | 5 ++--- .../monthly_attendance_sheet.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 2d0e8857cab..33132a6898a 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -89,11 +89,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-20 11:20:54.255557", + "modified": "2020-05-23 12:41:52.543438", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -127,7 +126,7 @@ "type": "DocType" }, { - "label": "Salary Register", + "label": "Monthly Attendance Sheet", "link_to": "Monthly Attendance Sheet", "type": "Report" }, 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 60767b5db08..47daab19017 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -135,7 +135,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 - ggg = [] + emp_status_map = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -152,11 +152,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho status = "Holiday" total_h += 1 - ggg.append(status_map.get(status, "")) + abbr = status_map.get(status, "") + emp_status_map.append(abbr) - if not filters.summarized_view: - row += ggg - else: + if filters.summarized_view: if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": @@ -170,6 +169,9 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho elif not status: total_um += 1 + if not filters.summarized_view: + row += emp_status_map + if filters.summarized_view: row += [total_p, total_l, total_a, total_h, total_um] @@ -203,7 +205,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) - emp_att_map[emp] = ggg + emp_att_map[emp] = emp_status_map record.append(row) return record, emp_att_map @@ -216,7 +218,7 @@ def get_columns(filters): columns = [_(filters.group_by)+ ":Link/Branch:120"] columns += [ - _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" + _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Data/:120" ] days = [] for day in range(filters["total_days_in_month"]): From ee70d7a7ebff2ec22b24bf9f3e3264e4aa01fcd6 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Tue, 26 May 2020 17:02:51 +0530 Subject: [PATCH 276/410] fix: fix function import (#21927) --- erpnext/patches/v7_0/update_mins_to_first_response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v7_0/update_mins_to_first_response.py b/erpnext/patches/v7_0/update_mins_to_first_response.py index 1df4b42cedb..16681357e68 100644 --- a/erpnext/patches/v7_0/update_mins_to_first_response.py +++ b/erpnext/patches/v7_0/update_mins_to_first_response.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe -from frappe.core.doctype.communication.email import update_mins_to_first_communication +from frappe.core.doctype.communication.communication import update_mins_to_first_communication def execute(): frappe.reload_doctype('Issue') From 74a910ae92b52b4ca515d738a489c41a3e3f5bc2 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 26 May 2020 17:49:34 +0530 Subject: [PATCH 277/410] fix: title for onboarding step (#21933) Co-authored-by: Shivam Mishra Co-authored-by: Shivam Mishra --- .../introduction_to_stock_entry.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json index 447611fe475..009a44f6e4d 100644 --- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:55:41.457289", + "modified": "2020-05-26 15:55:41.457289", "modified_by": "Administrator", "name": "Introduction to Stock Entry", "owner": "Administrator", "show_full_form": 0, - "title": "Introduction to the multi-purpose stock transaction", + "title": "Introduction to Stock Entry", "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" } \ No newline at end of file From c52bbd79bf02be46248c5daa100d80168c59f151 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 17:54:46 +0530 Subject: [PATCH 278/410] fix(Healthcare): set company in healthcare service unit setup (#21929) --- erpnext/healthcare/setup.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index 2087f49f32f..06840801d37 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -195,10 +195,21 @@ def create_sensitivity(): def add_healthcare_service_unit_tree_root(): record = [ - { - "doctype": "Healthcare Service Unit", - "healthcare_service_unit_name": "All Healthcare Service Units", - "is_group": 1 - } + { + "doctype": "Healthcare Service Unit", + "healthcare_service_unit_name": "All Healthcare Service Units", + "is_group": 1, + "company": get_company() + } ] insert_record(record) + +def get_company(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company = frappe.get_list("Company", limit=1) + if company: + return company[0].name + return None From ddc170521f896567a1e2d875628432a6ab0c0abe Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 26 May 2020 17:55:56 +0530 Subject: [PATCH 279/410] fix: check for warehouse in the woocommerce settings (#21925) --- .../erpnext_integrations/connectors/woocommerce_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 44f87e0462e..1b0c9f60b6e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -182,7 +182,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr) - if not frappe.db.exists("Warehouse", default_warehouse): + if not frappe.db.exists("Warehouse", default_warehouse) \ + and not woocommerce_settings.warehouse: frappe.throw(_("Please set Warehouse in Woocommerce Settings")) for item in order.get("line_items"): From 7402451b9695f97937d534ae649c4029491cd36a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 26 May 2020 17:56:40 +0530 Subject: [PATCH 280/410] fix: shopify error message on failure of sales order creation (#21924) --- erpnext/erpnext_integrations/connectors/shopify_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index ca0e1609cb0..7046038fb24 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): items = get_order_items(shopify_order.get("line_items"), shopify_settings) if not items: - message = 'Following items are exists in order but relevant record not found in Product master' + message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' message += "\n" + ", ".join(product_not_exists) - make_shopify_log(status="Error", exception=e, rollback=True) + make_shopify_log(status="Error", exception=message, rollback=True) return '' From 93e15c5f5c7581047929407fa599cce5abaaea43 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:00:37 +0530 Subject: [PATCH 281/410] fix: showing Wrong balance on allocation boundary dates (#21904) --- .../employee_leave_balance.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 97be5cd813c..db1d191758f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -3,13 +3,13 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt +from frappe.utils import flt, add_days from frappe import _ -from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on, get_leave_allocation_records +from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('"From date" can not be greater than or equal to "To date"')) columns = get_columns() data = get_data(filters) @@ -104,14 +104,17 @@ def get_data(filters): new_allocation, expired_leaves = get_allocated_and_expired_leaves(filters.from_date, filters.to_date, employee.name, leave_type) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) + opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) #allocation boundary condition row.leaves_allocated = new_allocation row.leaves_expired = expired_leaves - leaves_taken if expired_leaves - leaves_taken > 0 else 0 row.opening_balance = opening row.leaves_taken = leaves_taken - row.closing_balance = closing + + # not be shown on the basis of days left it create in user mind for carry_forward leave + row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken)) + + row.indent = 1 data.append(row) new_leaves_allocated = 0 @@ -177,7 +180,7 @@ def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type): }, as_dict=1) for record in records: - if record.to_date <= getdate(to_date): + if record.to_date < getdate(to_date): expired_leaves += record.leaves if record.from_date >= getdate(from_date): From 752ca63fea5f722e0bc119a4f303e42b056b5b70 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:01:03 +0530 Subject: [PATCH 282/410] fix: Was showing opening on date not remaimning leave (#21899) --- .../employee_leave_balance_summary.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index a5cdecf36a7..92715d34453 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ - import get_leave_balance_on, get_leaves_for_period + import get_leave_details from erpnext.hr.report.employee_leave_balance.employee_leave_balance \ import get_department_leave_approver_map @@ -61,14 +61,14 @@ def get_data(filters, leave_types): if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)): row = [employee.name, employee.employee_name, employee.department] - + available_leave = get_leave_details(employee.name, filters.date) for leave_type in leave_types: - + remaining = 0 + if leave_type in available_leave["leave_allocation"]: # opening balance - opening = get_leave_balance_on(employee.name, leave_type, filters.date) + remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves'] - - row += [opening] + row += [remaining] data.append(row) From 5fa207f0200048bd521443d32f157b29969fdf03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 26 May 2020 18:02:21 +0530 Subject: [PATCH 283/410] fix(UAE): Incorrect VAT rate display in Sales Invoice (#21882) --- erpnext/regional/united_arab_emirates/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a01c6ceec36..772bbf5914b 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals +import frappe from frappe.utils import flt from erpnext.controllers.taxes_and_totals import get_itemised_tax +from six import iteritems def update_itemised_tax_data(doc): if not doc.taxes: return @@ -9,7 +11,14 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - if itemised_tax.get(row.item_code): + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + if item_tax_rate: + for account, rate in iteritems(item_tax_rate): + tax_rate += rate + elif itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From 56357fafeb9bad805e466a5dc9c1ca29295ed192 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:08:22 +0530 Subject: [PATCH 284/410] fix: Added column Expired Leave (#21857) --- .../doctype/leave_application/leave_application.py | 11 ++++++++++- .../leave_application_dashboard.html | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 84f2c836cc6..f2968bcd889 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -438,14 +438,23 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + + total_allocated_leaves = frappe.db.get_value('Leave Allocation', { + 'from_date': ('<=', date), + 'to_date': ('>=', date), + 'leave_type': allocation.leave_type, + }, 'SUM(total_leaves_allocated)') or 0 + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) + end_date = allocation.to_date leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, + "total_leaves": total_allocated_leaves, + "expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken), "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 2385b6ac1c4..295f3b43419 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,12 @@ - - - - - + + + + + + @@ -17,6 +18,7 @@ + From d28854b3c23d83c95c3f461a8a2aa35f5082c490 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 26 May 2020 18:13:25 +0530 Subject: [PATCH 285/410] fix: throw error if no serial numbers are found in Pick List (#21915) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 1f8d009f925..93b29c8daff 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -24,6 +24,9 @@ class PickList(Document): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): continue + if not item.serial_no: + frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') From c0b4ec52de7e2c03fc0e478825750d2c060b618b Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 26 May 2020 18:20:47 +0530 Subject: [PATCH 286/410] fix: UX date range field separated in from and to date fields. (#21765) * fix for issue #21419 * changing group by filter to default Group by Voucher (Consolidated) * divided date range to seperate from and to date * divided date range to seperate from and to date for purcase and sales register --- .../item_wise_purchase_register.js | 17 ++++++++++++----- .../item_wise_purchase_register.py | 1 - .../item_wise_sales_register.js | 17 ++++++++++++----- .../item_wise_sales_register.py | 1 - 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js index f88906a0f1f..b709ab9b57d 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Purchase Register"] = { "filters": [ { - "fieldname":"date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "item_code", 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 1f78c7a006f..9777ed1dfde 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 @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 8a9c76f26f8..39fb3ca5ee3 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Sales Register"] = { "filters": [ { - "fieldname": "date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "customer", 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 92a22e62f14..bb78ee2d675 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 @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") From 49b2b155b6122ade55bb723c2d979a96e568446d Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Tue, 26 May 2020 18:21:11 +0530 Subject: [PATCH 287/410] fix: sanitize all-products search before displaying results (#21764) Signed-off-by: Chinmay D. Pai --- erpnext/www/all-products/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html index f09021412b7..0126b59c64a 100644 --- a/erpnext/www/all-products/index.html +++ b/erpnext/www/all-products/index.html @@ -11,7 +11,7 @@
    From 18e7b60acc103ef505afd2c0880576bf9d143288 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 26 May 2020 18:37:02 +0530 Subject: [PATCH 288/410] feat(Selling): Gross Profit In Quotation (#21795) * added valuation rate and gross_profit in quotation item table * modified code to not fetch valuation_rate manually, used set_gross_profit from selling controller --- erpnext/controllers/selling_controller.py | 2 +- erpnext/public/js/controllers/transaction.js | 2 +- .../selling/doctype/quotation/quotation.py | 4 +-- .../quotation_item/quotation_item.json | 34 ++++++++++++++++++- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 1e0a48c134b..b696ac39f69 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -361,7 +361,7 @@ class SellingController(StockController): self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) def set_gross_profit(self): - if self.doctype == "Sales Order": + if self.doctype in ["Sales Order", "Quotation"]: for item in self.items: item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 637d3b3267d..524a95804fd 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1711,7 +1711,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, set_gross_profit: function(item) { - if (this.frm.doc.doctype == "Sales Order" && item.valuation_rate) { + if (["Sales Order", "Quotation"].includes(this.frm.doc.doctype) && item.valuation_rate) { var rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1); item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item)); } diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 7cfec5a046a..0e771c3025a 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -197,9 +197,9 @@ def set_expired_status(): cond = "qo.docstatus = 1 and qo.status != 'Expired' and qo.valid_till < %s" # check if those QUO have SO against it so_against_quo = """ - SELECT + SELECT so.name FROM `tabSales Order` so, `tabSales Order Item` so_item - WHERE + WHERE so_item.docstatus = 1 and so.docstatus = 1 and so_item.parent = so.name and so_item.prevdoc_docname = qo.name""" diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index d50397cfad4..59ae7b23239 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -48,6 +48,10 @@ "base_net_amount", "pricing_rules", "is_free_item", + "section_break_43", + "valuation_rate", + "column_break_45", + "gross_profit", "item_weight_details", "weight_per_unit", "total_weight", @@ -602,12 +606,40 @@ "label": "Against Blanket Order", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "section_break_43", + "fieldtype": "Section Break" + }, + { + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "label": "Valuation Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break" + }, + { + "fieldname": "gross_profit", + "fieldtype": "Currency", + "label": "Gross Profit", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-30 18:40:28.782720", + "modified": "2020-05-19 20:48:43.222229", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", From 11ea0b1289288f61c1a4744b1d1983527c9239ab Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:23:45 +0530 Subject: [PATCH 289/410] fix: Do not add filters in report on accounting dimension creation if it already exists --- .../accounts_payable/accounts_payable.js | 9 +-------- .../accounts_payable_summary.js | 9 +-------- .../accounts_receivable/accounts_receivable.js | 9 +-------- .../accounts_receivable_summary.js | 9 +-------- .../report/balance_sheet/balance_sheet.js | 2 ++ erpnext/accounts/report/cash_flow/cash_flow.js | 2 ++ .../report/general_ledger/general_ledger.js | 9 +-------- .../profit_and_loss_statement.js | 2 ++ .../purchase_register/purchase_register.js | 9 +-------- .../report/sales_register/sales_register.js | 9 +-------- .../report/trial_balance/trial_balance.js | 9 +-------- erpnext/public/js/financial_statements.js | 11 +---------- erpnext/public/js/utils.js | 18 ++++++++++++++++++ 13 files changed, 33 insertions(+), 74 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 4e09f99ae3e..2aa9618e559 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable', 9); diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index d5f18b09827..9c6b0639c0a 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 6208eb6946b..8dc558a611d 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable', 9); diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index b316f108d04..32ecc63ab33 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -122,11 +122,4 @@ frappe.query_reports["Accounts Receivable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c4c24c0bab3..4a4ad4d71cb 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -4,6 +4,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index e5d0c899181..a984bf46b50 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Cash Flow', 10); + // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 2aecd6b717d..1fc0f794788 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -164,12 +164,5 @@ frappe.query_reports["General Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('General Ledger', 15) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 2b946c05401..1c461efbcd3 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "project", diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index b2b95b2b81b..f34ea571639 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 9dee656d4a9..85bbceab827 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Sales Register', 7); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 07752e1e626..9c0854c5d3a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions('Trial Balance', 6); }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 296c6280d84..cf98b7534e9 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -62,7 +62,7 @@ erpnext.financial_statements = { } }; -function get_filters(){ +function get_filters() { let filters = [ { "fieldname":"company", @@ -162,15 +162,6 @@ function get_filters(){ } ] - erpnext.dimension_filters.forEach((dimension) => { - filters.push({ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); - return filters; } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58969f2a9fd..3eeafe124cc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,6 +145,7 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { + console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; @@ -191,6 +192,23 @@ $.extend(erpnext.utils, { }) }, + add_dimensions: function(report_name, index) { + let filters = frappe.query_reports[report_name].filters; + + erpnext.dimension_filters.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); + + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } + }); + }, + make_subscription: function(doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", From bcee78d054ddda75421c7c0ea98e4427683cd087 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:26:47 +0530 Subject: [PATCH 290/410] fix: Remove console statement --- erpnext/public/js/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 3eeafe124cc..2cd79b5d5e7 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,7 +145,6 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { - console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; From 1e04b455f07173f8f5522e47d6ef7d9035e39990 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 26 May 2020 23:07:11 +0530 Subject: [PATCH 291/410] fix: manufacturing dashboard and work order summary chart --- erpnext/manufacturing/dashboard_fixtures.py | 42 ++++++++++--------- .../downtime_entry/downtime_entry.json | 11 ++++- .../downtime_analysis/downtime_analysis.py | 11 +++-- .../work_order_summary/work_order_summary.py | 11 ++--- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index ef61f230acd..4a17fd07fbb 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -3,7 +3,7 @@ import frappe, erpnext, json from frappe import _ -from frappe.utils import nowdate, get_date_str +from frappe.utils import nowdate, get_first_day, get_last_day, add_months from erpnext.accounts.utils import get_fiscal_year def get_data(): @@ -28,10 +28,10 @@ def get_dashboards(): { "chart": "Job Card Analysis", "width": "Full" } ], "cards": [ - { "card": "Total Work Order" }, - { "card": "Completed Work Order" }, + { "card": "Monthly Total Work Order" }, + { "card": "Monthly Completed Work Order" }, { "card": "Ongoing Job Card" }, - { "card": "Total Quality Inspection"} + { "card": "Monthly Quality Inspection"} ] }] @@ -180,38 +180,37 @@ def get_charts(): }] def get_number_cards(): - fiscal_year = get_fiscal_year(date=nowdate()) - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + start_date = add_months(nowdate(), -1) + end_date = nowdate() return [{ "doctype": "Number Card", "document_type": "Work Order", - "name": "Total Work Order", + "name": "Monthly Total Work Order", "filters_json": json.dumps([ ['Work Order', 'docstatus', '=', 1], - ['Work Order', 'creation', 'between', [year_start_date, year_end_date]] + ['Work Order', 'creation', 'between', [start_date, end_date]] ]), "function": "Count", "is_public": 1, - "label": _("Total Work Order"), + "label": _("Monthly Total Work Order"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", "document_type": "Work Order", - "name": "Completed Work Order", + "name": "Monthly Completed Work Order", "filters_json": json.dumps([ ['Work Order', 'status', '=', 'Completed'], ['Work Order', 'docstatus', '=', 1], - ['Work Order', 'creation', 'between', [year_start_date, year_end_date]] + ['Work Order', 'creation', 'between', [start_date, end_date]] ]), "function": "Count", "is_public": 1, - "label": _("Completed Work Order"), + "label": _("Monthly Completed Work Order"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", @@ -225,16 +224,19 @@ def get_number_cards(): "is_public": 1, "label": _("Ongoing Job Card"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", "document_type": "Quality Inspection", - "name": "Total Quality Inspection", - "filters_json": json.dumps([['Quality Inspection', 'docstatus', '=', 1]]), + "name": "Monthly Quality Inspection", + "filters_json": json.dumps([ + ['Quality Inspection', 'docstatus', '=', 1], + ['Quality Inspection', 'creation', 'between', [start_date, end_date]] + ]), "function": "Count", "is_public": 1, - "label": _("Total Quality Inspection"), + "label": _("Monthly Quality Inspection"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }] \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json index 9acb4f05133..b301a9ec054 100644 --- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -1,11 +1,13 @@ { "actions": [], "allow_import": 1, + "autoname": "naming_series:", "creation": "2020-04-18 04:50:46.187638", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "workstation", "operator", "column_break_4", @@ -78,10 +80,17 @@ "fieldname": "remarks", "fieldtype": "Text", "label": "Remarks" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "DT-", + "reqd": 1 } ], "links": [], - "modified": "2020-05-19 12:59:37.358483", + "modified": "2020-05-26 22:14:54.479831", "modified_by": "Administrator", "module": "Manufacturing", "name": "Downtime Entry", diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index 2b2be4faa3b..093309a005b 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -24,7 +24,12 @@ def get_data(filters): if filters.get("workstation"): query_filters["workstation"] = filters.get("workstation") - return frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) + data = frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) or [] + for d in data: + if d.downtime: + d.downtime = d.downtime / 60 + + return data def get_chart_data(data, columns): labels = sorted(list(set([d.workstation for d in data]))) @@ -44,7 +49,7 @@ def get_chart_data(data, columns): "data": { "labels": labels, "datasets": [ - {"name": "Dataset 1", "values": datasets} + {"name": "Machine Downtime", "values": datasets} ] }, "type": "bar" @@ -88,7 +93,7 @@ def get_columns(filters): "width": 160 }, { - "label": _("Downtime (In Mins)"), + "label": _("Downtime (In Hours)"), "fieldname": "downtime", "fieldtype": "Float", "width": 150 diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index bc09ed43354..fb047b230ce 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -56,7 +56,7 @@ def get_chart_data(data, filters): return get_chart_based_on_qty(data, filters) def get_chart_based_on_status(data): - labels = ["Not Started", "In Process", "Stopped", "Completed"] + labels = ["Completed", "In Process", "Stopped", "Not Started"] status_wise_data = { "Not Started": 0, @@ -66,13 +66,10 @@ def get_chart_based_on_status(data): } for d in data: - if d.status == "In Process" and d.produced_qty: - status_wise_data["Completed"] += d.produced_qty + status_wise_data[d.status] += 1 - status_wise_data[d.status] += d.qty - - values = [status_wise_data["Not Started"], status_wise_data["In Process"], - status_wise_data["Stopped"], status_wise_data["Completed"]] + values = [status_wise_data["Completed"], status_wise_data["In Process"], + status_wise_data["Stopped"], status_wise_data["Not Started"]] chart = { "data": { From e4bbdbbfe1ed5d490f7ce6e46b7bde437c741ccf Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 06:17:26 +0000 Subject: [PATCH 292/410] fix: get data from request directly (#21954) --- erpnext/non_profit/doctype/membership/membership.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 5a69cdb6ab0..df19995a1c5 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -65,7 +65,9 @@ def get_member_based_on_subscription(subscription_id, email): return frappe.get_doc("Member", members[0]['name']) @frappe.whitelist(allow_guest=True) -def trigger_razorpay_subscription(data): +def trigger_razorpay_subscription(*args, **kwargs): + data = frappe.request.get_data() + if isinstance(data, six.string_types): data = json.loads(data) data = frappe._dict(data) From 2a81058f90dde63020eb309648921206d9172ab0 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 06:23:48 +0000 Subject: [PATCH 293/410] refactor: project summary report (#21943) * feat: added more filters * feat: show only first 30 projects in chart * Update erpnext/projects/report/project_summary/project_summary.js Co-authored-by: Marica Co-authored-by: Marica --- .../report/project_summary/project_summary.js | 21 ++++++++++++++++++- .../report/project_summary/project_summary.py | 17 ++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js index 15367acd7d3..414b7b206a1 100644 --- a/erpnext/projects/report/project_summary/project_summary.js +++ b/erpnext/projects/report/project_summary/project_summary.js @@ -12,12 +12,31 @@ frappe.query_reports["Project Summary"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname": "is_active", + "label": __("Is Active"), + "fieldtype": "Select", + "options": "\nYes\nNo", + "default": "Yes", + }, { "fieldname": "status", "label": __("Status"), "fieldtype": "Select", - "options": "Open\nComplete\nCancelled", + "options": "\nOpen\nCompleted\nCancelled", "default": "Open" + }, + { + "fieldname": "project_type", + "label": __("Project Type"), + "fieldtype": "Link", + "options": "Project Type" + }, + { + "fieldname": "priority", + "label": __("Priority"), + "fieldtype": "Select", + "options": "\nLow\nMedium\nHigh" } ] }; diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index a20d7f25a3a..ea7f1ab2e77 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -9,7 +9,7 @@ def execute(filters=None): columns = get_columns() data = [] - data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date"], order_by="expected_end_date") + data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date", "project_type"], order_by="expected_end_date") for project in data: project["total_tasks"] = frappe.db.count("Task", filters={"project": project.name}) @@ -30,6 +30,13 @@ def get_columns(): "options": "Project", "width": 200 }, + { + "fieldname": "project_type", + "label": _("Type"), + "fieldtype": "Link", + "options": "Project Type", + "width": 120 + }, { "fieldname": "status", "label": _("Status"), @@ -88,19 +95,19 @@ def get_chart_data(data): return { "data": { - 'labels': labels, + 'labels': labels[:30], 'datasets': [ { "name": "Overdue", - "values": overdue + "values": overdue[:30] }, { "name": "Completed", - "values": completed + "values": completed[:30] }, { "name": "Total Tasks", - "values": total + "values": total[:30] }, ] }, From 01bced8c67313c36232a5e992e9ef5d3d2ea4476 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 12:26:54 +0530 Subject: [PATCH 295/410] fix Report not working on parameter Grade (#21951) --- .../hr/report/employee_analytics/employee_analytics.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index df64006c1be..8f393889269 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -26,7 +26,7 @@ def get_columns(): return [ _("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", _("Gender") + "::60", _("Company") + ":Link/Company:120" + _("Designation") + ":Link/Designation:120", _("Gender") + "::100", _("Company") + ":Link/Company:120" ] def get_conditions(filters): @@ -43,7 +43,12 @@ def get_employees(filters): gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) def get_parameters(filters): - return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + if filters.get("parameter") == "Grade": + parameter = "Employee Grade" + else: + parameter = filters.get("parameter") + + return frappe.db.sql("""select name from `tab"""+ parameter +"""` """, as_list=1) def get_chart_data(parameters,employees, filters): if not parameters: From ef0026c06f224a486978164325461abb81e32aac Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 12:28:11 +0530 Subject: [PATCH 296/410] fix: addtional salary date validation (#21952) --- erpnext/hr/doctype/additional_salary/additional_salary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index bab6fb545f8..e369ba7cef6 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -37,7 +37,7 @@ class AdditionalSalary(Document): frappe.throw(_("Payroll date can not be less than employee's joining date.")) elif getdate(self.from_date) < getdate(date_of_joining): frappe.throw(_("From date can not be less than employee's joining date.")) - elif getdate(self.to_date) > getdate(relieving_date): + elif relieving_date and getdate(self.to_date) > getdate(relieving_date): frappe.throw(_("To date can not be greater than employee's relieving date.")) def get_amount(self, sal_start_date, sal_end_date): From a18c896a5641fb74b7cb82bf6808a0b7dcdcfb6d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 27 May 2020 12:43:44 +0530 Subject: [PATCH 297/410] fix: titles and order of Healthcare Onboarding steps (#21948) * fix(Healthcare): title and order of onboarding steps * refactor: healthcare settings tour --- .../doctype/healthcare_settings/healthcare_settings.js | 10 +++++----- .../module_onboarding/healthcare/healthcare.json | 10 +++++----- .../create_healthcare_practitioner.json} | 8 ++++---- .../explore_clinical_procedure_templates.json | 2 +- .../explore_healthcare_settings.json | 2 +- .../introduction_to_healthcare_practitioner.json} | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) rename erpnext/healthcare/onboarding_step/{create_practitioner/create_practitioner.json => create_healthcare_practitioner/create_healthcare_practitioner.json} (64%) rename erpnext/healthcare/onboarding_step/{setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json => introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json} (67%) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index c266ba86477..cf2276fc07c 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -57,19 +57,19 @@ frappe.tour['Healthcare Settings'] = [ description: __('Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.') }, { - fieldname: 'healthcare_service_items', + fieldname: 'inpatient_visit_charge_item', title: __('Healthcare Service Items'), - description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + description: __('You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ') + "here" + __(' to know more') }, { - fieldname: 'sb_in_ac', + fieldname: 'income_account', title: __('Set up default Accounts for the Healthcare Facility'), description: __('If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.') }, { - fieldname: 'out_patient_sms_alerts', + fieldname: 'send_registration_msg', title: __('Out Patient SMS alerts'), - description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + description: __('If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ') + "here" + __(' to know more') } ]; diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index db35149f876..3e50726060a 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 12:52:09.757729", + "modified": "2020-05-26 23:16:37.603361", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -19,14 +19,14 @@ { "step": "Create Patient" }, - { - "step": "Create Practitioner" - }, { "step": "Create Practitioner Schedule" }, { - "step": "Setup Schedule and Employee for Healthcare Practitioner" + "step": "Introduction to Healthcare Practitioner" + }, + { + "step": "Create Healthcare Practitioner" }, { "step": "Explore Healthcare Settings" diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json similarity index 64% rename from erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json rename to erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json index 614b201a58f..c45a347080c 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json +++ b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-19 10:39:55.728057", + "creation": "2020-05-19 10:39:55.728058", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:27:39.851375", + "modified": "2020-05-26 23:16:31.965521", "modified_by": "Administrator", - "name": "Create Practitioner", + "name": "Create Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", "show_full_form": 1, - "title": "Create Practitioner", + "title": "Create Healthcare Practitioner", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json index f0c0f612e18..697b761e528 100644 --- a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json +++ b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 11:46:35.085270", + "modified": "2020-05-26 23:10:24.504030", "modified_by": "Administrator", "name": "Explore Clinical Procedure Templates", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json index 2bdab69faa3..b2d5aef4312 100644 --- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -8,7 +8,7 @@ "is_mandatory": 1, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 12:26:48.682673", + "modified": "2020-05-26 23:10:24.507648", "modified_by": "Administrator", "name": "Explore Healthcare Settings", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json similarity index 67% rename from erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json rename to erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json index c5af177e347..fa4c9036d7e 100644 --- a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json @@ -9,12 +9,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:26:42.492734", + "modified": "2020-05-26 22:07:07.482530", "modified_by": "Administrator", - "name": "Setup Schedule and Employee for Healthcare Practitioner", + "name": "Introduction to Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", "show_full_form": 0, - "title": "Setup Schedule and Employee for Healthcare Practitioner", + "title": "Introduction to Healthcare Practitioner", "validate_action": 0 } \ No newline at end of file From ed2c1803df5b0640276ff5ed92fb8a8f3480bc8c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 12:46:12 +0530 Subject: [PATCH 298/410] fix: Post Dated unallocated amount not considered in Advance Amount in AR/AP summary (#21837) * fix: Post Dated unallocateed amount not considered in Advance Amount in AR/AP summary report * fix: Add future payment filter in AR/AP summary * fix: Show unallocated future payments only till current creation date * fix: Remove extra query * fix: Remove debug * fix: Condition --- erpnext/accounts/party.py | 8 ++++++-- .../accounts_receivable/accounts_receivable.py | 13 ++++++++++--- .../accounts_receivable_summary.js | 7 ++++++- .../accounts_receivable_summary.py | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 528fb4e113a..db91b6696eb 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -602,10 +602,14 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None): cond = "1=1" if posting_date: - cond = "posting_date <= '{0}'".format(posting_date) + if future_payment: + cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date) + else: + cond = "posting_date <= '{0}'".format(posting_date) + if company: cond += "and company = '{0}'".format(company) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a0a1b9783ac..c776d4781e3 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -559,6 +559,14 @@ class ReceivablePayableReport(object): conditions, values = self.prepare_conditions() order_by = self.get_order_by_condition() + if self.filters.show_future_payments: + values.insert(2, self.filters.report_date) + + date_condition = """AND (posting_date <= %s + OR (against_voucher IS NULL AND DATE(creation) <= %s))""" + else: + date_condition = "AND posting_date <=%s" + if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" else: @@ -574,9 +582,8 @@ class ReceivablePayableReport(object): docstatus < 2 and party_type=%s and (party is not null and party != '') - and posting_date <= %s - {1} {2}""" - .format(select_fields, conditions, order_by), values, as_dict=True) + {1} {2} {3}""" + .format(select_fields, date_condition, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index 32ecc63ab33..305cddb102a 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", - } + }, + { + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), + "fieldtype": "Check", + }, ], onload: function(report) { diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index aa6b42e89d0..657b3e8f204 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date, self.filters.company) or {} + self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: From 07388495f36c389830277d7a21124593f91d8cb9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 27 May 2020 12:57:16 +0530 Subject: [PATCH 299/410] fix: label changed in production plan --- .../manufacturing/doctype/production_plan/production_plan.js | 4 ++-- .../report/bom_operations_time/bom_operations_time.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 64c952b67be..1a64bc5e248 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -201,9 +201,9 @@ frappe.ui.form.on('Production Plan', { title: title, fields: [ { - "fieldtype": "Table MultiSelect", "label": __("Source Warehouses"), + "fieldtype": "Table MultiSelect", "label": __("Source Warehouses (Optional)"), "fieldname": "warehouses", "options": "Production Plan Material Request Warehouse", - "description": "System will pickup the materials from the selected warehouses", + "description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."), get_query: function () { return { filters: { diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py index 1279011b222..e7d92658f7d 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py @@ -103,7 +103,7 @@ def get_columns(filters): "fieldtype": "Int", "width": 140 }, { - "label": _("Subassembly BOM Count"), + "label": _("Sub-assembly BOM Count"), "fieldname": "used_as_subassembly_items", "fieldtype": "Int", "width": 180 From 0fa1cc647f824cd996823496a098c83011830295 Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Wed, 27 May 2020 09:06:35 +0100 Subject: [PATCH 300/410] fix: website_list_for_contact, fix changed frappe.db.exist keyword argument (#21958) --- erpnext/controllers/website_list_for_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed379389d7e..ecf041efd17 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False): return frappe.db.exists(doctype, get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.db.exists(doctype, filters={ + return frappe.db.exists(doctype, { 'name': doc.name, fieldname: ["in", suppliers] }) From cb1a910538c6234910a1c1c05ea1cd30adb36272 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 27 May 2020 14:47:46 +0530 Subject: [PATCH 301/410] fix: filters for the manufacturing reports --- .../job_card_summary/job_card_summary.js | 29 ++++++++++++++++--- .../job_card_summary/job_card_summary.py | 29 +++++++++++++++---- .../work_order_summary/work_order_summary.js | 29 ++++++++++++++++--- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index b7e307183f8..33953b1265e 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -13,17 +13,38 @@ frappe.query_reports["Job Card Summary"] = { reqd: 1 }, { - label: __("From Date"), + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: frappe.defaults.get_user_default("fiscal_year"), + reqd: 1, + on_change: function(query_report) { + var fiscal_year = query_report.get_values().fiscal_year; + if (!fiscal_year) { + return; + } + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + from_date: fy.year_start_date, + to_date: fy.year_end_date + }); + }); + } + }, + { + label: __("From Posting Date"), fieldname:"from_date", fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + default: frappe.defaults.get_user_default("year_start_date"), reqd: 1 }, { - label: __("To Date"), + label: __("To Posting Datetime"), fieldname:"to_date", fieldtype: "Date", - default: frappe.datetime.get_today(), + default: frappe.defaults.get_user_default("year_end_date"), reqd: 1, }, { diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index ae1e4f30469..953d8201a74 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -15,9 +15,12 @@ def execute(filters=None): return columns, data, None, chart_data def get_data(filters): - query_filters = {"docstatus": ("<", 2)} + query_filters = { + "docstatus": ("=", 1), + "posting_date": ("between", [filters.from_date, filters.to_date]) + } - fields = ["name", "status", "work_order", "production_item", "item_name", + fields = ["name", "status", "work_order", "production_item", "item_name", "posting_date", "total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"] for field in ["work_order", "workstation", "operation", "company"]: @@ -30,12 +33,19 @@ def get_data(filters): if not data: return [] job_cards = [d.name for d in data] + + job_card_time_filter = { + "docstatus": 1, + "parent": ("in", job_cards), + } + job_card_time_details = {} for job_card_data in frappe.get_all("Job Card Time Log", fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"], - filters={"docstatus": ("<", 2), "parent": ("in", job_cards)}, group_by="parent"): + filters=job_card_time_filter, group_by="parent", debug=1): job_card_time_details[job_card_data.parent] = job_card_data + res = [] for d in data: if d.status == "Material Transferred": d.status = "Open" @@ -43,8 +53,9 @@ def get_data(filters): if job_card_time_details.get(d.name): d.from_time = job_card_time_details.get(d.name).from_time d.to_time = job_card_time_details.get(d.name).to_time + res.append(d) - return data + return res def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) @@ -86,10 +97,10 @@ def prepare_chart_data(job_card_details, filters): labels.append(period) for d in job_card_details: - if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: + if getdate(d.posting_date) > from_date and getdate(d.posting_date) <= end_date: status = "Completed" if d.status == "Completed" else "Pending" - if periodic_data.get(status) and periodic_data.get(status).get(period): + if periodic_data.get(status).get(period): periodic_data[status][period] += 1 else: periodic_data[status][period] = 1 @@ -105,6 +116,12 @@ def get_columns(filters): "options": "Job Card", "width": 100 }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 100 + }, ] if not filters.get("status"): diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index ec9fe35d63a..22928657b9b 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -13,17 +13,38 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - label: __("From Date"), + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: frappe.defaults.get_user_default("fiscal_year"), + reqd: 1, + on_change: function(query_report) { + var fiscal_year = query_report.get_values().fiscal_year; + if (!fiscal_year) { + return; + } + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + from_date: fy.year_start_date, + to_date: fy.year_end_date + }); + }); + } + }, + { + label: __("From Posting Date"), fieldname:"from_date", fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + default: frappe.defaults.get_user_default("year_start_date"), reqd: 1 }, { - label: __("To Date"), + label: __("To Posting Datetime"), fieldname:"to_date", fieldtype: "Date", - default: frappe.datetime.get_today(), + default: frappe.defaults.get_user_default("year_end_date"), reqd: 1, }, { From 45571b99a834642018dd727172ad0efc3fee7851 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 26 May 2020 20:29:08 +0530 Subject: [PATCH 302/410] feat: added tour to manufacturing settings --- .../manufacturing_settings.js | 28 +++++++++++++++++++ .../explore_manufacturing_settings.json | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js index ac144e24b10..668e981d188 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js @@ -3,3 +3,31 @@ frappe.ui.form.on('Manufacturing Settings', { }); + +frappe.tour["Manufacturing Settings"] = [ + { + fieldname: "material_consumption", + title: __("Allow Multiple Material Consumption"), + description: __("If ticked, multiple materials can be used for a single Work Order. This is useful if one or more time consuming products are being manufactured.") + }, + { + fieldname: "backflush_raw_materials_based_on", + title: __("Backflush Raw Materials"), + description: __("The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

    When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field.") + }, + { + fieldname: "default_wip_warehouse", + title: __("Work In Progress Warehouse"), + description: __("This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders.") + }, + { + fieldname: "default_fg_warehouse", + title: __("Finished Goods Warehouse"), + description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order.") + }, + { + fieldname: "update_bom_costs_automatically", + title: __("Update BOM Cost Automatically"), + description: __("If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.") + } +]; \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json index 582aba40d68..7ef202ee4e7 100644 --- a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json +++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json @@ -1,5 +1,5 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-19 11:55:11.378374", "docstatus": 0, "doctype": "Onboarding Step", @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 12:12:28.145366", + "modified": "2020-05-26 20:28:03.558199", "modified_by": "Administrator", "name": "Explore Manufacturing Settings", "owner": "Administrator", From a77032f926fde8244854715f6506e1f081fb3b95 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 27 May 2020 20:20:20 +0530 Subject: [PATCH 303/410] onbarding steps fix (#21959) --- erpnext/crm/module_onboarding/crm/crm.json | 2 +- .../create_and_send_quotation.json | 6 +++--- erpnext/crm/onboarding_step/create_lead/create_lead.json | 6 +++--- .../create_opportunity/create_opportunity.json | 2 +- .../introduction_to_crm/introduction_to_crm.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 694763f7c7f..9b3d91ecee8 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-20 12:53:47.029412", + "modified": "2020-05-27 11:33:09.941263", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index a6edfd7e53d..9201d77cf87 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:30:07.887411", + "modified": "2020-05-27 11:30:28.237263", "modified_by": "Administrator", "name": "Create and Send Quotation", "owner": "Administrator", "reference_document": "Quotation", - "show_full_form": 0, + "show_full_form": 1, "title": "Create and Send Quotation", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index 47a45d70a8c..6ff0bd61f12 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:28:36.441387", + "modified": "2020-05-27 11:30:59.493720", "modified_by": "Administrator", "name": "Create Lead", "owner": "Administrator", "reference_document": "Lead", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Lead", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json index 231cf17bb57..9f996d9e2be 100644 --- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -15,5 +15,5 @@ "reference_document": "Opportunity", "show_full_form": 0, "title": "Create Opportunity", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index 552ade0fdda..545a756a596 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:28:16.448676", + "modified": "2020-05-27 11:28:07.452857", "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", "show_full_form": 0, "title": "Introduction to CRM", - "validate_action": 0, + "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" } \ No newline at end of file From d6d84a3c1561195810c6e1da9cdd16ad7ff62b18 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 20:21:15 +0530 Subject: [PATCH 304/410] fix: Module Onboarding for HR (#21963) --- .../create_holiday_list/create_holiday_list.json | 4 ++-- .../onboarding_step/create_leave_type/create_leave_type.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json index 25cb9febd00..208e394560e 100644 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-14 11:47:34.700174", + "creation": "2020-05-27 11:47:34.700174", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -13,7 +13,7 @@ "name": "Create Holiday list", "owner": "Administrator", "reference_document": "Holiday List", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Holiday list", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json index e8b97c2e5ba..8cbfc5c81f9 100644 --- a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json +++ b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-20 11:17:31.119312", + "creation": "2020-05-27 11:17:31.119312", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -13,7 +13,7 @@ "name": "Create Leave Type", "owner": "Administrator", "reference_document": "Leave Type", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Leave Type", "validate_action": 0 } \ No newline at end of file From 873542bc7f57f6627d6a906f133ea179785a3795 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:26:38 +0530 Subject: [PATCH 305/410] fix: Do not allow backdated stock transactions in previous fiscal year (#21967) --- erpnext/utilities/transaction_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index ea96503dff9..024aa6f31dd 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -166,7 +166,7 @@ class TransactionBase(StatusUpdater): last_transaction_time = frappe.db.sql(""" select MAX(timestamp(posting_date, posting_time)) as posting_time from `tabStock Ledger Entry` - where docstatus = 1 and fiscal_year = %s""", (fiscal_year))[0][0] + where docstatus = 1""")[0][0] cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") From 0030b95595e2a74b0167a5e791d62371af4bb24a Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:27:33 +0530 Subject: [PATCH 306/410] fix: Filtering issues in opneing invoice creation tool (#21969) --- .../opening_invoice_creation_tool.js | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 4d8da37efe0..699eb08e178 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -11,21 +11,9 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { }; }); - frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { - return { - filters: { - 'company': doc.company - } - }; - }); - - frm.set_query('cost_center', function(doc) { - return { - filters: { - 'company': doc.company - } - }; - }); + if (frm.doc.company) { + frm.trigger('setup_company_filters'); + } }, refresh: function(frm) { @@ -51,19 +39,50 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { }); }, - company: function(frm) { - frappe.call({ - method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', - args: { - company: frm.doc.company - }, - callback: (r) => { - if (r.message) { - frm.doc.__onload.temporary_opening_account = r.message; - frm.trigger('update_invoice_table'); + setup_company_filters: function(frm) { + frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { + return { + filters: { + 'company': doc.company + } + }; + }); + + frm.set_query('cost_center', function(doc) { + return { + filters: { + 'company': doc.company + } + }; + }); + + frm.set_query('temporary_opening_account', 'invoices', function(doc, cdt, cdn) { + return { + filters: { + 'company': doc.company } } - }) + }); + }, + + company: function(frm) { + if (frm.doc.company) { + + frm.trigger('setup_company_filters'); + + frappe.call({ + method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', + args: { + company: frm.doc.company + }, + callback: (r) => { + if (r.message) { + frm.doc.__onload.temporary_opening_account = r.message; + frm.trigger('update_invoice_table'); + } + } + }) + } }, invoice_type: function(frm) { From a49f045d1435d1fb01547ccba48da4ef00b42710 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 27 May 2020 20:28:37 +0530 Subject: [PATCH 307/410] fix: Grammar fixes (#21970) --- erpnext/buying/dashboard_fixtures.py | 6 +++--- erpnext/buying/desk_page/buying/buying.json | 8 ++++---- erpnext/stock/desk_page/stock/stock.json | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 0e2f78f3479..186bfb23af9 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -41,7 +41,7 @@ def get_dashboards(): { "chart": "Top Suppliers", "width": "Full"} ], "cards": [ - { "card": "This Year Purchases"}, + { "card": "Annual Purchase"}, { "card": "Purchase Orders to Receive"}, { "card": "Purchase Orders to Bill"}, { "card": "Active Suppliers"} @@ -142,7 +142,7 @@ def get_charts(): def get_number_cards(): return [ { - "name": "This Year Purchases", + "name": "Annual Purchase", "aggregate_function_based_on": "base_net_total", "doctype": "Number Card", "document_type": "Purchase Order", @@ -155,7 +155,7 @@ def get_number_cards(): ]), "function": "Sum", "is_public": 1, - "label": _("This Year Purchases"), + "label": _("Annual Purchase"), "owner": "Administrator", "show_percentage_stats": 1, "stats_time_interval": "Monthly" diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index ee18545fd44..d31602b607e 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -55,7 +55,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-19 19:44:36.260982", + "modified": "2020-05-27 19:55:22.407528", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -66,7 +66,7 @@ "shortcuts": [ { "color": "#cef6d1", - "format": "{} available", + "format": "{} Available", "label": "Item", "link_to": "Item", "stats_filter": "{\n \"disabled\": 0\n}", @@ -82,7 +82,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Receive", + "format": "{} To Receive", "label": "Purchase Order", "link_to": "Purchase Order", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", @@ -99,7 +99,7 @@ "type": "Report" }, { - "label": "Buying Dashboard", + "label": "Dashboard", "link_to": "Buying", "type": "Dashboard" } diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 4506664c1ed..b1835a2adf9 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-19 17:36:08.185652", + "modified": "2020-05-27 19:56:28.396962", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -69,7 +69,7 @@ "shortcuts": [ { "color": "#cef6d1", - "format": "{} available", + "format": "{} Available", "label": "Item", "link_to": "Item", "stats_filter": "{\n \"disabled\" : 0\n}", @@ -90,7 +90,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill", + "format": "{} To Bill", "label": "Purchase Receipt", "link_to": "Purchase Receipt", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", @@ -98,7 +98,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill", + "format": "{} To Bill", "label": "Delivery Note", "link_to": "Delivery Note", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", @@ -115,7 +115,7 @@ "type": "Report" }, { - "label": "Stock Dashboard", + "label": "Dashboard", "link_to": "Stock", "type": "Dashboard" } From 152377f84d7de8b8a0d20d970660ee107d5eca26 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:29:10 +0530 Subject: [PATCH 308/410] fix: Total for ageing column 121-Above (#21972) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c776d4781e3..d40e58b5287 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -534,7 +534,7 @@ class ReceivablePayableReport(object): def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] - row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 + row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 if not (self.age_as_on and entry_date): return From 8fcbf1b90e141e59f8072916abe74089497afcd9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 15:01:30 +0000 Subject: [PATCH 309/410] fix: compare start and end time to prevent negative diff (#21974) * fix: compare start and end time to prevent negative diff * feat: parse date when comparing --- erpnext/manufacturing/doctype/job_card/job_card.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e43b98aee1a..d97714a23ee 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -102,8 +102,11 @@ class JobCard(Document): workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) if (not workstation_doc.working_hours or cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))): - row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, - row.planned_start_time) + if getdate(row.planned_end_time) < getdate(row.planned_start_time): + row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.time_in_mins) + row.remaining_time_in_mins = 0.0 + else: + row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, row.planned_start_time) self.update_time_logs(row) return From cb1a7d0fbc54757e6a72d013cd38d4213dd24ae1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 20:36:28 +0530 Subject: [PATCH 310/410] fix: use get_datetime instead of getdate --- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index d97714a23ee..c29d4ba3d5c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -102,7 +102,7 @@ class JobCard(Document): workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) if (not workstation_doc.working_hours or cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))): - if getdate(row.planned_end_time) < getdate(row.planned_start_time): + if get_datetime(row.planned_end_time) < get_datetime(row.planned_start_time): row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.time_in_mins) row.remaining_time_in_mins = 0.0 else: From c2aabb0fbf0efe4491afe68ba1ab13efe4ae0d6f Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 27 May 2020 20:56:39 +0530 Subject: [PATCH 311/410] fix: Buying Module fixes (#21965) * fix: Buying Module fixes * fix: Delete Report files as well * fix: Add Purchase Order Analysis to Stock & Accounting --- .../desk_page/accounting/accounting.json | 5 +- .../__init__.py | 0 .../purchase_order_items_to_be_billed.js | 8 --- .../purchase_order_items_to_be_billed.json | 33 ------------ .../purchase_order_items_to_be_billed.py | 26 --------- .../module_onboarding/buying/buying.json | 4 +- .../requested_items_to_be_ordered/__init__.py | 0 .../requested_items_to_be_ordered.json | 31 ----------- .../requested_items_to_order.js | 14 ++++- .../requested_items_to_order.py | 54 +++++++++++++------ erpnext/patches.txt | 1 + .../v13_0/delete_old_purchase_reports.py | 15 ++++++ erpnext/stock/desk_page/stock/stock.json | 4 +- .../__init__.py | 0 .../purchase_order_items_to_be_received.json | 34 ------------ .../__init__.py | 0 ..._order_items_to_be_received_or_billed.json | 34 ------------ 17 files changed, 74 insertions(+), 189 deletions(-) delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py delete mode 100644 erpnext/buying/report/requested_items_to_be_ordered/__init__.py delete mode 100644 erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json create mode 100644 erpnext/patches/v13_0/delete_old_purchase_reports.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received/__init__.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 576d10c0247..42fb9f4f37d 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Accounts Payable", - "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Billed\",\n \"name\": \"Purchase Order Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -94,10 +94,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-18 17:27:26.882340", + "modified": "2020-05-27 20:34:50.949772", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py b/erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js deleted file mode 100644 index 24c9592c241..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Purchase Order Items To Be Billed"] = { - "filters": [ - - ] -} diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json deleted file mode 100644 index 3645ec02e1d..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-05-28 15:54:16", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:00:24.302988", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Purchase Order Items To Be Billed", - "owner": "Administrator", - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`supplier_name` as \"Supplier Name::150\",\n\t`tabPurchase Order Item`.`project` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Closed\"\n and `tabPurchase Order Item`.amount > 0\n\tand (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) < `tabPurchase Order Item`.base_amount\norder by `tabPurchase Order`.transaction_date asc", - "ref_doctype": "Purchase Invoice", - "report_name": "Purchase Order Items To Be Billed", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Purchase User" - }, - { - "role": "Auditor" - }, - { - "role": "Accounts Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py deleted file mode 100644 index 99d0a368139..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data - -def execute(filters=None): - columns = get_column() - args = get_args() - data = get_ordered_to_be_billed_data(args) - return columns, data - -def get_column(): - return [ - _("Purchase Order") + ":Link/Purchase Order:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120", - _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100", - _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", - ] - -def get_args(): - return {'doctype': 'Purchase Order', 'party': 'supplier', - 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 8c798b34731..8fe2f388b0c 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 20:03:55.776080", + "modified": "2020-05-27 17:17:52.075947", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -49,6 +49,6 @@ ], "subtitle": "Products, Purchases, Analysis and more.", "success_message": "The Buying Module is all set up!", - "title": "Let's Setup the Buying Module.", + "title": "Let's Set Up the Buying Module.", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_be_ordered/__init__.py b/erpnext/buying/report/requested_items_to_be_ordered/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json b/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json deleted file mode 100644 index bb112698d32..00000000000 --- a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2013-05-13 16:10:02", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2019-04-18 19:02:03.099422", - "modified_by": "Administrator", - "module": "Buying", - "name": "Requested Items To Be Ordered", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc", - "ref_doctype": "Purchase Order", - "report_name": "Requested Items To Be Ordered", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock User" - }, - { - "role": "Purchase Manager" - }, - { - "role": "Purchase User" - } - ] - } \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js index 21adb135476..9555e8252a3 100644 --- a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js @@ -35,7 +35,7 @@ frappe.query_reports["Requested Items to Order"] = { "fieldtype": "Link", "width": "80", "options": "Material Request", - "get_query": () =>{ + "get_query": () => { return { filters: { "docstatus": 1, @@ -45,6 +45,18 @@ frappe.query_reports["Requested Items to Order"] = { } } }, + { + "fieldname": "item_code", + "label": __("Item"), + "fieldtype": "Link", + "width": "80", + "options": "Item", + "get_query": () => { + return { + query: "erpnext.controllers.queries.item_query" + } + } + }, { "fieldname": "group_by_mr", "label": __("Group by Material Request"), diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py index a021d3c1cab..cca01b104ae 100644 --- a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py @@ -44,6 +44,9 @@ def get_conditions(filters): if filters.get("material_request"): conditions += " and mr.name = '{0}'".format(filters.get("material_request")) + if filters.get("item_code"): + conditions += " and mr_item.item_code = '{0}'".format(filters.get("item_code")) + return conditions def get_data(filters, conditions): @@ -74,25 +77,41 @@ def get_data(filters, conditions): return data +def update_qty_columns(row_to_update, data_row): + fields = ["qty", "ordered_qty", "qty_to_order"] + for field in fields: + row_to_update[field] += flt(data_row[field]) + def prepare_data(data, filters): """Prepare consolidated Report data and Chart data""" - material_request_map = {} + material_request_map, item_qty_map = {}, {} for row in data: - if not row["material_request"] in material_request_map: - # create an entry with mr as key - row_copy = copy.deepcopy(row) - material_request_map[row["material_request"]] = row_copy + # item wise map for charts + if not row["item_code"] in item_qty_map: + item_qty_map[row["item_code"]] = { + "qty" : row["qty"], + "ordered_qty" : row["ordered_qty"], + "qty_to_order" : row["qty_to_order"] + } else: - mr_row = material_request_map[row["material_request"]] - mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) + item_entry = item_qty_map[row["item_code"]] + update_qty_columns(item_entry, row) - #sum numeric rows - fields = ["qty", "ordered_qty", "qty_to_order"] - for field in fields: - mr_row[field] = flt(mr_row[field]) + flt(row[field]) + if filters.get("group_by_mr"): + # consolidated material request map for group by filter + if not row["material_request"] in material_request_map: + # create an entry with mr as key + row_copy = copy.deepcopy(row) + material_request_map[row["material_request"]] = row_copy + else: + mr_row = material_request_map[row["material_request"]] + mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) - chart_data = prepare_chart_data(material_request_map) + #sum numeric columns + update_qty_columns(mr_row, row) + + chart_data = prepare_chart_data(item_qty_map) if filters.get("group_by_mr"): data =[] @@ -102,12 +121,15 @@ def prepare_data(data, filters): return data, chart_data -def prepare_chart_data(data): +def prepare_chart_data(item_data): labels, qty_to_order, ordered_qty = [], [], [] - for row in data: - mr_row = data[row] - labels.append(mr_row["material_request"]) + if len(item_data) > 30: + item_data = dict(list(item_data.items())[:30]) + + for row in item_data: + mr_row = item_data[row] + labels.append(row) qty_to_order.append(mr_row["qty_to_order"]) ordered_qty.append(mr_row["ordered_qty"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 929f8d6b98b..b0b224f084d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -693,3 +693,4 @@ erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor +erpnext.patches.v13_0.delete_old_purchase_reports diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py new file mode 100644 index 00000000000..8271d2e6dce --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -0,0 +1,15 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + reports_to_delete = ["Requested Items To Be Ordered", + "Purchase Order Items To Be Received or Billed","Purchase Order Items To Be Received", + "Purchase Order Items To Be Billed"] + + for report in reports_to_delete: + if frappe.db.exists("Report", report): + frappe.delete_doc("Report", report) \ No newline at end of file diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index b1835a2adf9..9404292c04f 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Received\",\n \"name\": \"Purchase Order Items To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-27 19:56:28.396962", + "modified": "2020-05-27 20:38:25.255323", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/report/purchase_order_items_to_be_received/__init__.py b/erpnext/stock/report/purchase_order_items_to_be_received/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json b/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json deleted file mode 100644 index dfaa9ed6cc7..00000000000 --- a/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2013-02-22 18:01:55", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2019-04-01 22:12:05.573343", - "modified_by": "Administrator", - "module": "Stock", - "name": "Purchase Order Items To Be Received", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`status` as \"Status:Data:120\",\n\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order Item`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`supplier_name` as \"Supplier Name::150\",\n\t`tabPurchase Order Item`.`project` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Order Item`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive:Float:100\",\n `tabPurchase Order Item`.warehouse as \"Warehouse:Link/Warehouse:150\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n `tabPurchase Order Item`.brand as \"Brand::100\",\n\t`tabPurchase Order`.`company` as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status not in (\"Stopped\", \"Closed\")\n\tand ifnull(`tabPurchase Order Item`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", - "ref_doctype": "Purchase Receipt", - "report_name": "Purchase Order Items To Be Received", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock Manager" - }, - { - "role": "Stock User" - }, - { - "role": "Purchase User" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json deleted file mode 100644 index 48c0f423fd9..00000000000 --- a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2019-09-16 14:10:33.102865", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-09-21 15:19:55.710578", - "modified_by": "Administrator", - "module": "Stock", - "name": "Purchase Order Items To Be Received or Billed", - "owner": "Administrator", - "prepared_report": 0, - "query": "SELECT\n\t`poi_pri`.`purchase_order` as \"Purchase Order:Link/Purchase Order:120\",\n\t`poi_pri`.`status` as \"Status:Data:120\",\n\t`poi_pri`.`transaction_date` as \"Date:Date:100\",\n\t`poi_pri`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`poi_pri`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`poi_pri`.`supplier_name` as \"Supplier Name::150\",\n\t`poi_pri`.`item_code` as \"Item Code:Link/Item:120\",\n\t`poi_pri`.`qty` as \"Qty:Float:100\",\n\t`poi_pri`.`base_amount` as \"Base Amount:Currency:100\",\n\t`poi_pri`.`received_qty` as \"Received Qty:Float:100\",\n\t`poi_pri`.`received_amount` as \"Received Qty Amount:Currency:100\",\n\t`poi_pri`.`qty_to_receive` as \"Qty to Receive:Float:100\",\n\t`poi_pri`.`amount_to_be_received` as \"Amount to Receive:Currency:100\",\n\t`poi_pri`.`billed_amount` as \"Billed Amount:Currency:100\",\n\t`poi_pri`.`amount_to_be_billed` as \"Amount To Be Billed:Currency:100\",\n\tSUM(`pii`.`qty`) AS \"Billed Qty:Float:100\",\n\t`poi_pri`.qty - SUM(`pii`.`qty`) AS \"Qty To Be Billed:Float:100\",\n\t`poi_pri`.`warehouse` as \"Warehouse:Link/Warehouse:150\",\n\t`poi_pri`.`item_name` as \"Item Name::150\",\n\t`poi_pri`.`description` as \"Description::200\",\n\t`poi_pri`.`brand` as \"Brand::100\",\n\t`poi_pri`.`project` as \"Project\",\n\t`poi_pri`.`company` as \"Company:Link/Company:\"\nFROM\n\t(SELECT\n\t\t`po`.`name` AS 'purchase_order',\n\t\t`po`.`status`,\n\t\t`po`.`company`,\n\t\t`poi`.`warehouse`,\n\t\t`poi`.`brand`,\n\t\t`poi`.`description`,\n\t\t`po`.`transaction_date`,\n\t\t`poi`.`schedule_date`,\n\t\t`po`.`supplier`,\n\t\t`po`.`supplier_name`,\n\t\t`poi`.`project`,\n\t\t`poi`.`item_code`,\n\t\t`poi`.`item_name`,\n\t\t`poi`.`qty`,\n\t\t`poi`.`base_amount`,\n\t\t`poi`.`received_qty`,\n\t\t(`poi`.billed_amt * ifnull(`po`.conversion_rate, 1)) as billed_amount,\n\t\t(`poi`.base_amount - (`poi`.billed_amt * ifnull(`po`.conversion_rate, 1))) as amount_to_be_billed,\n\t\t`poi`.`qty` - IFNULL(`poi`.`received_qty`, 0) AS 'qty_to_receive',\n\t\t(`poi`.`qty` - IFNULL(`poi`.`received_qty`, 0)) * `poi`.`rate` AS 'amount_to_be_received',\n\t\tSUM(`pri`.`amount`) AS 'received_amount',\n\t\t`poi`.`name` AS 'poi_name',\n\t\t`pri`.`name` AS 'pri_name'\n\tFROM\n\t\t`tabPurchase Order` po\n\t\tLEFT JOIN `tabPurchase Order Item` poi\n\t\tON `poi`.`parent` = `po`.`name`\n\t\tLEFT JOIN `tabPurchase Receipt Item` pri\n\t\tON `pri`.`purchase_order_item` = `poi`.`name`\n\t\t\tAND `pri`.`docstatus`=1\n\tWHERE\n\t\t`po`.`status` not in ('Stopped', 'Closed')\n\t\tAND `po`.`docstatus` = 1\n\t\tAND IFNULL(`poi`.`received_qty`, 0) < IFNULL(`poi`.`qty`, 0)\n\tGROUP BY `poi`.`name`\n\tORDER BY `po`.`transaction_date` ASC\n\t) poi_pri\n\tLEFT JOIN `tabPurchase Invoice Item` pii\n\tON `pii`.`po_detail` = `poi_pri`.`poi_name`\n\t\tAND `pii`.`docstatus`=1\nGROUP BY `poi_pri`.`poi_name`", - "ref_doctype": "Purchase Order", - "report_name": "Purchase Order Items To Be Received or Billed", - "report_type": "Query Report", - "roles": [ - { - "role": "Purchase Manager" - }, - { - "role": "Purchase User" - }, - { - "role": "Stock User" - }, - { - "role": "Stock Manager" - } - ] -} \ No newline at end of file From 7ac731dd4f212c404a0099aa97fb51824e9d4ce6 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 27 May 2020 21:53:17 +0530 Subject: [PATCH 312/410] fix: raw material warehouse in Production Planning Report (#21982) --- .../production_planning_report/production_planning_report.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py index b5e6c6fc853..5ac3923187a 100644 --- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -220,6 +220,9 @@ class ProductionPlanReport(object): if item_details: warehouses = [item_details["default_warehouse"]] + if self.filters.raw_material_warehouse: + warehouses = get_child_warehouses(self.filters.raw_material_warehouse) + d.remaining_qty = d.required_qty self.pick_materials_from_warehouses(d, data, warehouses) From 3da51984f2a31fe6e43fcb91a9b8162f262ca66f Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 25 May 2020 21:46:04 +0530 Subject: [PATCH 313/410] chore: Add Import Supplier Invoice to Menu --- erpnext/config/buying.py | 21 +++++++- .../import_supplier_invoice.json | 48 +++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 16b49a1e578..b06bb76ca82 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +import frappe from frappe import _ def get_data(): - return [ + config = [ { "label": _("Purchasing"), "icon": "fa fa-star", @@ -243,3 +244,21 @@ def get_data(): }, ] + + regional = { + "label": _("Regional"), + "items": [ + { + "type": "doctype", + "name": "Import Supplier Invoice", + "description": _("Import Italian Supplier Invoice."), + "onboard": 1, + } + ] + } + + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + config.append(regional) + return config \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 59e955c23f4..6b45841b465 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2019-10-15 12:33:21.845329", "doctype": "DocType", "editable_grid": 1, @@ -92,13 +91,54 @@ "label": "Upload XML Invoices" } ], - "links": [], - "modified": "2019-12-10 16:37:26.793398", + "modified": "2020-05-25 21:32:49.064579", "modified_by": "Administrator", "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, + { + "permlevel": 1, + "read": 1, + "role": "Accounts Manager", + "write": 1 + } + ], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 From 29d7a7ea05cc5a0ed65b4252acad7c698a05fdef Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 21:53:01 +0530 Subject: [PATCH 314/410] fix: Added permission via regional setup and patch --- erpnext/patches.txt | 1 + ...ian_import_supplier_invoice_permissions.py | 12 +++++ .../import_supplier_invoice.json | 44 +------------------ erpnext/regional/italy/setup.py | 20 +++++++++ 4 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0b224f084d..b0421f43c64 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -694,3 +694,4 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py new file mode 100644 index 00000000000..a6011c4dace --- /dev/null +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.italy.setup import add_permissions + +def execute(): + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + add_permissions() \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 6b45841b465..c1680c4b492 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -96,49 +96,7 @@ "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User" - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor" - }, - { - "permlevel": 1, - "read": 1, - "role": "Accounts Manager", - "write": 1 - } - ], + "permissions": [], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 2d0ad66b0a0..6ab73413df2 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -7,11 +7,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options def setup(company=None, patch=True): make_custom_fields() setup_report() + add_permissions() def make_custom_fields(update=True): invoice_item_fields = [ @@ -200,3 +202,21 @@ def setup_report(): dict(role='Accounts Manager') ] )).insert() + +def add_permissions(): + doctype = 'Import Supplier Invoice' + add_permission(doctype, 'All', 0) + + for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'print', 1) + update_permission_property(doctype, role, 0, 'report', 1) + + if role in ('Accounts Manager', 'Accounts User'): + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + + update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1) + add_permission(doctype, 'Accounts Manager', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1) \ No newline at end of file From 50956aa29edf635f3d1db06bb055383f85ab85c4 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 22:24:39 +0530 Subject: [PATCH 315/410] chore: Added to v13 desk under Regional Link Card --- erpnext/buying/desk_page/buying/buying.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index d31602b607e..31b26d4ff89 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -34,6 +34,11 @@ "hidden": 0, "label": "Other Reports", "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Regional", + "links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]" } ], "cards_label": "Masters & Reports ", @@ -55,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-27 19:55:22.407528", + "modified": "2020-05-27 22:23:38.529720", "modified_by": "Administrator", "module": "Buying", "name": "Buying", From 33a08f1ea710ac0f24fa4406f3455942e6d32bb3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 27 May 2020 22:44:50 +0530 Subject: [PATCH 316/410] fix: TDS computation summary report --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 4d43919f62e..83d7967f793 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No if company: condition += "and company =%s" % (company) if from_date and to_date: - condition += "and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (from_date, to_date) ## Appending the same supplier again if length of suppliers list is 1 ## since tuple of single element list contains None, For example ('Test Supplier 1', ) From 234a55c6bc28ed1950d286182e70ded5eb7048e7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 09:55:24 +0530 Subject: [PATCH 317/410] refactor: showing the order's data for past period --- .../exponential_smoothing_forecasting.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index 1b49df6af87..cac80677297 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -21,8 +21,6 @@ class ExponentialSmoothingForecast(object): if value.get(period.key) and not forecast_data: value[forecast_key] = flt(value.get("avg", 0)) or flt(value.get(period.key)) - # will be use to forecaset next period - forecast_data.append([value.get(period.key), value.get(forecast_key)]) elif forecast_data: previous_period_data = forecast_data[-1] value[forecast_key] = (previous_period_data[1] + @@ -31,6 +29,10 @@ class ExponentialSmoothingForecast(object): ) ) + if value.get(forecast_key): + # will be use to forecaset next period + forecast_data.append([value.get(period.key), value.get(forecast_key)]) + class ForecastingReport(ExponentialSmoothingForecast): def __init__(self, filters=None): self.filters = frappe._dict(filters or {}) @@ -78,7 +80,9 @@ class ForecastingReport(ExponentialSmoothingForecast): list_of_period_value = [value.get(p.key, 0) for p in self.period_list] if list_of_period_value: - value["avg"] = sum(list_of_period_value) / len(list_of_period_value) + total_qty = [1 for d in list_of_period_value if d] + if total_qty: + value["avg"] = flt(sum(list_of_period_value)) / flt(sum(total_qty)) def get_data_for_forecast(self): cond = "" @@ -152,15 +156,19 @@ class ForecastingReport(ExponentialSmoothingForecast): "width": 130 }] - width = 150 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 + width = 180 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 for period in self.period_list: if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] or period.from_date >= getdate(self.filters.from_date)): - forecast_key = 'forecast_' + period.key + forecast_key = period.key + label = _(period.label) + if period.from_date >= getdate(self.filters.from_date): + forecast_key = 'forecast_' + period.key + label = _(period.label) + " " + _("(Forecast)") columns.append({ - "label": _(period.label), + "label": label, "fieldname": forecast_key, "fieldtype": self.fieldtype, "width": width, From 5226403b6297faf550c71b4c0ecfc58f6466d6e1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 10:10:22 +0530 Subject: [PATCH 318/410] refactor: display draft job card as Open job card --- .../job_card_summary/job_card_summary.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index 953d8201a74..b1bff3500c6 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -16,7 +16,7 @@ def execute(filters=None): def get_data(filters): query_filters = { - "docstatus": ("=", 1), + "docstatus": ("<", 2), "posting_date": ("between", [filters.from_date, filters.to_date]) } @@ -35,7 +35,7 @@ def get_data(filters): job_cards = [d.name for d in data] job_card_time_filter = { - "docstatus": 1, + "docstatus": ("<", 2), "parent": ("in", job_cards), } @@ -47,27 +47,28 @@ def get_data(filters): res = [] for d in data: - if d.status == "Material Transferred": + if d.status != "Completed": d.status = "Open" if job_card_time_details.get(d.name): d.from_time = job_card_time_details.get(d.name).from_time d.to_time = job_card_time_details.get(d.name).to_time - res.append(d) + + res.append(d) return res def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) - pending, completed = [], [] + open_job_cards, completed = [], [] datasets = [] for d in labels: - pending.append(periodic_data.get("Pending").get(d)) + open_job_cards.append(periodic_data.get("Open").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Open", "values": open_job_cards}) datasets.append({"name": "Completed", "values": completed}) chart = { @@ -84,7 +85,7 @@ def prepare_chart_data(job_card_details, filters): labels = [] periodic_data = { - "Pending": {}, + "Open": {}, "Completed": {} } @@ -98,7 +99,7 @@ def prepare_chart_data(job_card_details, filters): for d in job_card_details: if getdate(d.posting_date) > from_date and getdate(d.posting_date) <= end_date: - status = "Completed" if d.status == "Completed" else "Pending" + status = "Completed" if d.status == "Completed" else "Open" if periodic_data.get(status).get(period): periodic_data[status][period] += 1 From f7beeff2d8633538372a9bf992d9249869428d1f Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 11:05:19 +0530 Subject: [PATCH 319/410] fix: change description for field (#21995) grammatical fixes for description of Default Customer --- .../doctype/shopify_settings/shopify_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 8f1b746e5b6..d1e5f40d6c1 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -133,7 +133,7 @@ "label": "Customer Settings" }, { - "description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order", + "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", "fieldname": "default_customer", "fieldtype": "Link", "label": "Default Customer", @@ -277,4 +277,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From b86c0eb4043380a8b8c969a32493d82f790268bd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 12:07:07 +0530 Subject: [PATCH 320/410] fix: labels for date in manufacturing reports --- .../manufacturing/report/job_card_summary/job_card_summary.js | 2 +- .../report/work_order_summary/work_order_summary.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 33953b1265e..bd68db190e7 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Job Card Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 22928657b9b..eb23f17c477 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), From 603cc3d05e8729f9133ef30b228db53eb8377e15 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 28 May 2020 12:49:33 +0530 Subject: [PATCH 321/410] fix: transaction date not found in sales invoice --- erpnext/accounts/doctype/pricing_rule/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index cb05481df5a..53115f92d01 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype - date_field = ('transaction_date' - if doc.get('transaction_date') else 'posting_date') + date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date' child_doctype = '{0} Item'.format(doctype) apply_on = frappe.scrub(pr_doc.get('apply_on')) From 8653419c4cd5241a09f9bb34bdd269ad8fcdc0d1 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 12:52:58 +0530 Subject: [PATCH 322/410] fix: change modified time changed modified time to sync the changes --- .../doctype/shopify_settings/shopify_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index d1e5f40d6c1..5339c99155b 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -258,7 +258,7 @@ } ], "issingle": 1, - "modified": "2019-09-13 12:32:11.384757", + "modified": "2020-05-28 12:32:11.384757", "modified_by": "umair@erpnext.com", "module": "ERPNext Integrations", "name": "Shopify Settings", From dd39ba00140a348af397c899d93576a0f8d6ea6c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 28 May 2020 13:11:04 +0530 Subject: [PATCH 323/410] chore: tests --- .../doctype/pricing_rule/test_pricing_rule.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2da71dfd0e0..2bf0b725635 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") + + def test_cumulative_pricing_rule(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Cumulative Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "is_cumulative": 1, + "selling": 1, + "applicable_for": "Customer", + "customer": "_Test Customer", + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_amt": 0, + "max_amt": 10000, + "discount_percentage": 17.5, + "price_or_product_discount": "Price", + "company": "_Test Company", + "valid_from": frappe.utils.nowdate(), + "valid_upto": frappe.utils.nowdate() + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "_Test Item", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "name": None, + "transaction_date": frappe.utils.nowdate() + }) + details = get_item_details(args) + + self.assertTrue(details) def make_pricing_rule(**args): args = frappe._dict(args) From 706524f1ae4e89bd9f541c4b5e2db6b8a8bd7242 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 28 May 2020 13:33:58 +0530 Subject: [PATCH 324/410] fix: make transaction date of the oldest transaction as the last integration date (#22000) * fix: make transaction date of the oldest transaction as the last integration date * chore: remove blank line after docstring --- .../doctype/plaid_settings/plaid_settings.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a7062239c32..a45c6b13910 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -124,10 +124,11 @@ def add_account_subtype(account_subtype): @frappe.whitelist() def sync_transactions(bank, bank_account): - - last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") - if last_sync_date: - start_date = formatdate(last_sync_date, "YYYY-MM-dd") + '''Sync transactions based on the last integration date as the start date, after the sync is completed + add the transaction date of the oldest transaction as the last integration date''' + last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") + if last_transaction_date: + start_date = formatdate(last_transaction_date, "YYYY-MM-dd") else: start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd") @@ -139,12 +140,14 @@ def sync_transactions(bank, bank_account): for transaction in reversed(transactions): result += new_bank_transaction(transaction) - frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( - len(result), bank_account, start_date, end_date)) + if result: + end_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) + + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", end_date) - return result except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From f10771029a88f987795eb46e5081cb109b1a8d6f Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 28 May 2020 13:34:55 +0530 Subject: [PATCH 325/410] CRM dashboard fixes (#21989) --- erpnext/crm/dashboard_fixtures.py | 24 ++++++++++++++++++------ erpnext/crm/desk_page/crm/crm.json | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 16904b34295..0535cbbcc95 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -21,8 +21,8 @@ def get_dashboards(): { "chart": "Opportunity Trends", "width": "Full"}, { "chart": "Won Opportunities", "width": "Full" }, { "chart": "Territory Wise Opportunity Count", "width": "Half"}, - { "chart": "Territory Wise Sales", "width": "Half"}, { "chart": "Opportunities via Campaigns", "width": "Half" }, + { "chart": "Territory Wise Sales", "width": "Full"}, { "chart": "Lead Source", "width": "Half"} ], "cards": [ @@ -59,7 +59,7 @@ def get_charts(): 'is_public': 1, 'timeseries': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "filters_json": json.dumps([]), "type": "Bar" }, { @@ -90,7 +90,11 @@ def get_charts(): 'timeseries': 1, "owner": "Administrator", "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), - "type": "Pie" + "type": "Pie", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }, { "name": "Won Opportunities", @@ -123,7 +127,11 @@ def get_charts(): ["Opportunity", "company", "=", company, False] ]), "owner": "Administrator", - "type": "Donut" + "type": "Donut", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }, { "name": "Territory Wise Sales", @@ -140,7 +148,7 @@ def get_charts(): ["Opportunity", "company", "=", company, False], ["Opportunity", "status", "=", "Converted", False] ]), - "type": "Donut" + "type": "Bar" }, { "name": "Lead Source", @@ -152,7 +160,11 @@ def get_charts(): "document_type": "Lead", 'is_public': 1, "owner": "Administrator", - "type": "Pie" + "type": "Pie", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }] def get_number_cards(): diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 2fc4582917a..013fabef89f 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -42,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-20 12:11:36.250491", + "modified": "2020-05-28 13:29:28.253749", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -76,7 +76,7 @@ "type": "Report" }, { - "label": "CRM Dashboard", + "label": "Dashboard", "link_to": "CRM", "type": "Dashboard" } From cfcdd52bf4dd7dd17ae78673881a5afb3091fd22 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 May 2020 13:35:29 +0530 Subject: [PATCH 326/410] fix: Inter-company Invoice currency for multicurrency transactions (#21984) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 05b85dabd4f..57dc17936da 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1519,14 +1519,22 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): def update_details(source_doc, target_doc, source_parent): target_doc.inter_company_invoice_reference = source_doc.name if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: + currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.supplier = details.get("party") target_doc.buying_price_list = source_doc.selling_price_list + + if currency: + target_doc.currency = currency else: + currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.customer = details.get("party") target_doc.selling_price_list = source_doc.buying_price_list + if currency: + target_doc.currency = currency + doclist = get_mapped_doc(doctype, source_name, { doctype: { "doctype": target_doctype, From 6621e22c023319e17ee6b95d680825dc49b7723b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 28 May 2020 09:18:50 +0000 Subject: [PATCH 327/410] refactor: clean up desk pages (#22004) * refactor: clean up desk pages * refactor: update label for CRM --- erpnext/buying/desk_page/buying/buying.json | 8 +- erpnext/crm/desk_page/crm/crm.json | 4 +- erpnext/crm/module_onboarding/crm/crm.json | 4 +- erpnext/hr/desk_page/hr/hr.json | 20 ++-- .../loan_management.json => loan/loan.json} | 6 +- .../manufacturing/manufacturing.json | 6 +- .../projects/desk_page/projects/projects.json | 6 +- .../selling/desk_page/selling/selling.json | 94 +++++++++++++++++++ .../support/desk_page/support/support.json | 25 ++--- 9 files changed, 142 insertions(+), 31 deletions(-) rename erpnext/loan_management/desk_page/{loan_management/loan_management.json => loan/loan.json} (96%) create mode 100644 erpnext/selling/desk_page/selling/selling.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 31b26d4ff89..bddb9573ad5 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -41,7 +41,7 @@ "links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]" } ], - "cards_label": "Masters & Reports ", + "cards_label": "", "category": "Modules", "charts": [ { @@ -49,7 +49,7 @@ "label": "Purchase Order Trends" } ], - "charts_label": "Buying Dashboard", + "charts_label": "", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, @@ -60,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-27 22:23:38.529720", + "modified": "2020-05-28 13:32:49.960574", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -109,5 +109,5 @@ "type": "Dashboard" } ], - "shortcuts_label": "Quick Access" + "shortcuts_label": "" } \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 013fabef89f..eb69dc06b65 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -42,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-28 13:29:28.253749", + "modified": "2020-05-28 13:33:52.906750", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -52,6 +52,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffe8cd", "format": "{} Open", "label": "Lead", "link_to": "Lead", @@ -59,6 +60,7 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Assigned", "label": "Opportunity", "link_to": "Opportunity", diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 9b3d91ecee8..c43fcaef8c4 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-27 11:33:09.941263", + "modified": "2020-05-28 13:59:33.693420", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -37,6 +37,6 @@ ], "subtitle": "Lead, Opportunity, Customer and more", "success_message": "CRM Module is all setup!", - "title": "Let's Setup Your CRM", + "title": "Let's Set Up Your CRM", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 33132a6898a..7ac000b011a 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -89,10 +89,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-23 12:41:52.543438", + "modified": "2020-05-28 13:36:07.710600", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -102,6 +103,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#cef6d1", "format": "{} Active", "label": "Employee", "link_to": "Employee", @@ -109,17 +111,19 @@ "type": "DocType" }, { - "label": "Attendance", - "link_to": "Attendance", - "stats_filter": "", - "type": "DocType" - }, - { + "color": "#ffe8cd", + "format": "{} Open", "label": "Leave Application", "link_to": "Leave Application", "stats_filter": "{\"status\":\"Open\"}", "type": "DocType" }, + { + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, { "label": "Salary Structure", "link_to": "Payroll Entry", @@ -132,7 +136,7 @@ }, { "format": "{} Open", - "label": "HR Dashboard", + "label": "Dashboard", "link_to": "Human Resource", "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "Dashboard" diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan/loan.json similarity index 96% rename from erpnext/loan_management/desk_page/loan_management/loan_management.json rename to erpnext/loan_management/desk_page/loan/loan.json index d2a17630c9e..d79860a3520 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -34,10 +34,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-05-22 11:28:51.380509", + "modified": "2020-05-28 13:37:42.017709", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", @@ -46,8 +47,11 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffe8cd", + "format": "{} Open", "label": "Loan Application", "link_to": "Loan Application", + "stats_filter": "{ \"status\": \"Open\" }", "type": "DocType" }, { diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index f2e07bfe204..763f533a94b 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -47,7 +47,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-20 11:50:20.029056", + "modified": "2020-05-28 13:54:02.048419", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -58,6 +58,7 @@ "restrict_to_domain": "Manufacturing", "shortcuts": [ { + "color": "#cef6d1", "format": "{} Active", "label": "Item", "link_to": "Item", @@ -66,6 +67,7 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Active", "label": "BOM", "link_to": "BOM", @@ -74,6 +76,7 @@ "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Open", "label": "Work Order", "link_to": "Work Order", @@ -82,6 +85,7 @@ "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Open", "label": "Production Plan", "link_to": "Production Plan", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index fdbe13b2fb7..d91fe5304a3 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -33,7 +33,7 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-19 21:09:52.031828", + "modified": "2020-05-28 13:38:19.934937", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -42,7 +42,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#4d4da8", + "color": "#cef6d1", "format": "{} Assigned", "label": "Task", "link_to": "Task", @@ -50,7 +50,7 @@ "type": "DocType" }, { - "color": "#4d4da8", + "color": "#ffe8cd", "format": "{} Open", "label": "Project", "link_to": "Project", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json new file mode 100644 index 00000000000..c32a7c8481d --- /dev/null +++ b/erpnext/selling/desk_page/selling/selling.json @@ -0,0 +1,94 @@ +{ + "cards": [ + { + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Sales", + "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Key Reports", + "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" + } + ], + "category": "Modules", + "charts": [ + { + "chart_name": "Income", + "label": "Income" + } + ], + "creation": "2020-01-28 11:49:12.092882", + "developer_mode_only": 0, + "disable_user_customization": 0, + "docstatus": 0, + "doctype": "Desk Page", + "extends_another_page": 0, + "hide_custom": 1, + "idx": 0, + "is_standard": 1, + "label": "Selling", + "modified": "2020-05-28 13:46:08.314240", + "modified_by": "Administrator", + "module": "Selling", + "name": "Selling", + "owner": "Administrator", + "pin_to_bottom": 0, + "pin_to_top": 0, + "shortcuts": [ + { + "color": "#ffe8cd", + "format": "{} Draft", + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "stats_filter": "{ \"status\": \"Draft\" }", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} To Deliver", + "label": "Sales Order", + "link_to": "Sales Order", + "stats_filter": "{\"Status\": \"To Deliver and Bill\"}", + "type": "DocType" + }, + { + "color": "#cef6d1", + "format": "{} Open", + "label": "Quotation", + "link_to": "Quotation", + "stats_filter": "{ \"Status\": \"Open\" }", + "type": "DocType" + }, + { + "label": "Delivery Note", + "link_to": "Delivery Note", + "type": "DocType" + }, + { + "label": "Accounts Receivable", + "link_to": "Accounts Receivable", + "type": "Report" + }, + { + "label": "Sales Register", + "link_to": "Sales Register", + "type": "Report" + } + ] +} \ No newline at end of file diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 596987f46a5..a3fe72d0519 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -2,8 +2,8 @@ "cards": [ { "hidden": 0, - "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" + "label": "Issues", + "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -12,8 +12,8 @@ }, { "hidden": 0, - "label": "Issues", - "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]" + "label": "Service Level Agreement", + "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -39,11 +39,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-04-01 11:28:51.120583", + "modified": "2020-05-28 13:51:23.869954", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -52,19 +52,22 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffc4c4", + "format": "{} Assigned", "label": "Issue", "link_to": "Issue", - "type": "DocType" - }, - { - "label": "Service Level", - "link_to": "Service Level", + "stats_filter": "{\n \"_assign\": [\"like\", '%' + frappe.session.user + '%'],\n \"status\": \"Open\"\n}", "type": "DocType" }, { "label": "Maintenance Visit", "link_to": "Maintenance Visit", "type": "DocType" + }, + { + "label": "Service Level", + "link_to": "Service Level", + "type": "DocType" } ] } \ No newline at end of file From fd351f826853913a7fd57afb143b914915d187b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 15:18:47 +0530 Subject: [PATCH 328/410] fix: set fiscal year for Profit and Loss chart (#22006) (cherry picked from commit 4afda7601574a93e9232d06341556dbcf0f142ab) Co-authored-by: Shivam Mishra --- erpnext/accounts/dashboard_fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 1eed5a0f9ca..421c86dba01 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -60,9 +60,9 @@ def get_charts(): "report_name": "Profit and Loss Statement", "filters_json": json.dumps({ "company": company.name, - "filter_based_on": "Date Range", - "period_start_date": get_date_str(fiscal_year[1]), - "period_end_date": get_date_str(fiscal_year[2]), + "filter_based_on": "Fiscal Year", + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], "periodicity": "Monthly", "include_default_book_entries": 1 }), From 1a25aa83dec502e96467a9a0d6c9291308edc916 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 28 May 2020 18:16:24 +0530 Subject: [PATCH 329/410] fix: Delete Auto Email Reports (#22009) --- erpnext/patches/v13_0/delete_old_purchase_reports.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py index 8271d2e6dce..8bdc07ee5b8 100644 --- a/erpnext/patches/v13_0/delete_old_purchase_reports.py +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -12,4 +12,12 @@ def execute(): for report in reports_to_delete: if frappe.db.exists("Report", report): - frappe.delete_doc("Report", report) \ No newline at end of file + delete_auto_email_reports(report) + + frappe.delete_doc("Report", report) + +def delete_auto_email_reports(report): + """ Check for one or multiple Auto Email Reports and delete """ + auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) + for auto_email_report in auto_email_reports: + frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file From 517ab955ac5a577abf0dd9d612510b0873eaa2eb Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 28 May 2020 18:32:16 +0530 Subject: [PATCH 330/410] fix(rename_bank_reconcilliation): do not delete doc after renaming (#22013) --- erpnext/patches/v12_0/rename_bank_reconciliation.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation.py b/erpnext/patches/v12_0/rename_bank_reconciliation.py index eda47a95e03..2efa854fba9 100644 --- a/erpnext/patches/v12_0/rename_bank_reconciliation.py +++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py @@ -8,9 +8,6 @@ def execute(): if frappe.db.table_exists("Bank Reconciliation"): frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True) frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance') - + frappe.rename_doc('DocType', 'Bank Reconciliation Detail', 'Bank Clearance Detail', force=True) frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance Detail') - - frappe.delete_doc("DocType", "Bank Reconciliation") - frappe.delete_doc("DocType", "Bank Reconciliation Detail") From 1f3fe59e364d7ec16ed47f3a709df47bd75a9cac Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 May 2020 18:36:21 +0530 Subject: [PATCH 331/410] fix: Default period start date and period end date for financial statements (#22011) --- .../consolidated_financial_statement.js | 13 +++++++++++-- erpnext/public/js/financial_statements.js | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 38fd5fa2787..09479221fbb 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -33,7 +33,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -41,7 +40,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, @@ -106,5 +104,16 @@ frappe.query_reports["Consolidated Financial Statement"] = { value = $value.wrap("

    ").parent().html(); } return value; + }, + onload: function() { + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); } } diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index cf98b7534e9..d89d4712e6d 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -47,6 +47,16 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); + report.page.add_inner_button(__("Balance Sheet"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Balance Sheet', {company: filters.company}); @@ -99,7 +109,6 @@ function get_filters() { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -107,7 +116,6 @@ function get_filters() { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, From 9209b9a7dec0dbefdba918ba726580aa49e36b80 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 28 May 2020 18:49:47 +0530 Subject: [PATCH 332/410] fix(set_purchase_receipt_delivery_note_detail): commit after every 100 sql updates (#22016) --- .../set_purchase_receipt_delivery_note_detail.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py index 6f843cdabd7..52c9a2d7b3c 100644 --- a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py +++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py @@ -64,11 +64,13 @@ def execute(): return_document_map = make_return_document_map(doctype, return_document_map) + count = 0 + #iterate through original documents and its return documents for docname in return_document_map: - doc_items = frappe.get_doc(doctype, docname).get("items") + doc_items = frappe.get_cached_doc(doctype, docname).get("items") for return_doc in return_document_map[docname]: - return_doc_items = frappe.get_doc(doctype, return_doc).get("items") + return_doc_items = frappe.get_cached_doc(doctype, return_doc).get("items") #iterate through return document items and original document items for mapping for return_item in return_doc_items: @@ -80,9 +82,11 @@ def execute(): else: continue + # commit after every 100 sql updates + count += 1 + if count%100 == 0: + frappe.db.commit() + set_document_detail_in_return_document("Purchase Receipt") set_document_detail_in_return_document("Delivery Note") frappe.db.commit() - - - From a3dd75e77f6139f47dc142212550768d78197fee Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 19:03:37 +0530 Subject: [PATCH 333/410] fix: empty filters in Healthcare Charts --- erpnext/healthcare/dashboard_fixtures.py | 8 ++++---- erpnext/healthcare/desk_page/healthcare/healthcare.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 967117d22c2..94668a16d91 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -71,7 +71,7 @@ def get_charts(): "chart_name": _("Department wise Patient Appointments"), "chart_type": "Custom", "source": "Department wise Patient Appointments", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -159,7 +159,7 @@ def get_charts(): "document_type": "Patient Encounter Symptom", "group_by_type": "Count", "group_by_based_on": "complaint", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -173,7 +173,7 @@ def get_charts(): "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", "group_by_based_on": "diagnosis", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -229,7 +229,7 @@ def get_number_cards(): }, { "name": "Appointments to Bill", - "label": _("Appointments to Bill"), + "label": _("Appointments To Bill"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 60b53137cd7..334b65563bc 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -64,7 +64,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 20:57:22.797267", + "modified": "2020-05-28 19:02:28.824995", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -109,7 +109,7 @@ "type": "Page" }, { - "label": "Healthcare Dashboard", + "label": "Dashboard", "link_to": "Healthcare", "type": "Dashboard" } From 8385fed04293e16cf2017af8435ff48899707f13 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 28 May 2020 22:14:59 +0530 Subject: [PATCH 334/410] crm onboarding typos (#22008) --- erpnext/crm/module_onboarding/crm/crm.json | 8 ++++---- .../create_and_send_quotation.json | 2 +- erpnext/crm/onboarding_step/create_lead/create_lead.json | 2 +- .../introduction_to_crm/introduction_to_crm.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index c43fcaef8c4..44d672a7b59 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-28 13:59:33.693420", + "modified": "2020-05-28 21:07:41.278784", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -35,8 +35,8 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Lead, Opportunity, Customer and more", - "success_message": "CRM Module is all setup!", - "title": "Let's Set Up Your CRM", + "subtitle": "Lead, Opportunity, Customer and more.", + "success_message": "CRM Module is all Set Up!", + "title": "Let's Set Up Your CRM.", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 9201d77cf87..78f7e4de9c7 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:28.237263", + "modified": "2020-05-28 21:07:11.461172", "modified_by": "Administrator", "name": "Create and Send Quotation", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index 6ff0bd61f12..c45e8b036c5 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:59.493720", + "modified": "2020-05-28 21:07:01.373403", "modified_by": "Administrator", "name": "Create Lead", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index 545a756a596..fa26921ae2c 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:28:07.452857", + "modified": "2020-05-14 17:28:16.448676", "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", From 3c460364b5345ae823e150d532ee625b64475971 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 29 May 2020 10:18:06 +0530 Subject: [PATCH 335/410] fix: disposed asset creates inconsistencies in asset depr report (#22021) --- .../asset_depreciations_and_balances.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 80bccafd28e..5001ad9f12f 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -93,7 +93,7 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s then + ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ds.depreciation_amount else 0 @@ -115,9 +115,7 @@ def get_assets(filters): group by a.asset_category union SELECT a.asset_category, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 - and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) - then + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 else a.opening_accumulated_depreciation From cf0168668f40dfbfe10b782beb64d875fa29c4f5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 29 May 2020 10:19:33 +0530 Subject: [PATCH 336/410] fix: Correct filters (#22022) --- erpnext/hr/dashboard_fixtures.py | 2 +- .../create_holiday_list/create_holiday_list.json | 4 ++-- erpnext/hr/onboarding_step/hr_settings/hr_settings.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index 004477c26cc..6e042ac78d3 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -143,7 +143,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Employee", "Employees Left (Last year)", filters_json = json.dumps([ - ["Employee", "modified", "Previous", "1 year"], + ["Employee", "relieving_date", "Previous", "1 year"], ["Employee", "status", "=", "Left"] ]) ) diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json index 208e394560e..32472b4b3fa 100644 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-27 11:47:34.700174", + "creation": "2020-05-28 11:47:34.700174", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "Holiday List", "show_full_form": 1, - "title": "Create Holiday list", + "title": "Create Holiday List", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json index a8c96fb6827..0a1d0baf8aa 100644 --- a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -1,6 +1,6 @@ { "action": "Update Settings", - "creation": "2020-05-14 13:13:52.427711", + "creation": "2020-05-28 13:13:52.427711", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "HR Settings", "show_full_form": 0, - "title": "HR settings", + "title": "HR Settings", "validate_action": 0 } \ No newline at end of file From c302d066a3528c48ada9dc62df4e67f1c10b8d53 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 29 May 2020 10:19:58 +0530 Subject: [PATCH 337/410] fix: only save end date when transactions are returned (#22023) --- .../doctype/plaid_settings/plaid_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a45c6b13910..c3371ed5dfb 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -141,12 +141,12 @@ def sync_transactions(bank, bank_account): result += new_bank_transaction(transaction) if result: - end_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') + last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( len(result), bank_account, start_date, end_date)) - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", end_date) + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From cd8b5d1e4ce8ae809b6ad36b0ccdb89ee75ca4ce Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 29 May 2020 10:21:20 +0530 Subject: [PATCH 338/410] fix: cannot assign same task to other asset maintenance (#22024) --- .../asset_maintenance/asset_maintenance.json | 643 ++---------- .../asset_maintenance/asset_maintenance.py | 6 +- .../asset_maintenance_log.json | 967 ++++-------------- .../asset_maintenance_task.py | 3 +- 4 files changed, 295 insertions(+), 1324 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index efb2de3f266..c0c2566fe23 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,559 +1,140 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:asset_name", - "beta": 0, - "creation": "2017-10-19 16:50:22.879545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "Asset", - "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": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "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": "Asset Category", - "length": 0, - "no_copy": 0, - "options": "", - "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": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "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": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "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": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "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": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "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": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "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": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "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": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_serial_no", - "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": "Select Serial No", - "length": 0, - "no_copy": 0, - "options": "Serial No", - "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": "section_break_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "fieldtype": "Small Text", - "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": "Serial No", - "length": 0, - "no_copy": 0, - "options": "", - "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": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_team", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maintenance Team", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Team", - "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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "fieldtype": "Data", - "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": "Maintenance Manager", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "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": "Maintenance Manager Name", - "length": 0, - "no_copy": 0, - "options": "", - "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": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "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": "Tasks", - "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": "section_break_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance_tasks", - "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": "Maintenance Tasks", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "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": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-22 17:20:54.711885", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-28 20:28:32.993823", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index 3f046113a0f..d6adde6a371 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -38,7 +38,7 @@ class AssetMaintenance(Document): @frappe.whitelist() def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): - team_member = frappe.get_doc('User', assign_to_member).email + team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', 'assign_to' : team_member, @@ -77,7 +77,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las def update_maintenance_log(asset_maintenance, item_code, item_name, task): asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, - "task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) + "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])}) if not asset_maintenance_log: asset_maintenance_log = frappe.get_doc({ @@ -86,7 +86,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "asset_name": asset_maintenance, "item_code": item_code, "item_name": item_name, - "task": task.maintenance_task, + "task": task.name, "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 42aecdf44ac..7395bec1e62 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,819 +1,210 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-23 16:58:44.424309", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "assign_to_name", + "column_break_6", + "due_date", + "completion_date", + "maintenance_status", + "section_break_12", + "has_certificate", + "certificate_attachement", + "section_break_6", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance", - "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": "Asset Maintenance", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance", - "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": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, { - "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": 0, - "options": "ACC-AML-.YYYY.-", - "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": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "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": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "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_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "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": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "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": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "task", - "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": "Task", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "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": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "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": "Maintenance Type", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "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": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "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": "Assign To", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.next_due_date", - "fieldname": "due_date", - "fieldtype": "Date", - "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": "Due Date", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "completion_date", - "fieldtype": "Date", - "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": "Completion 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_status", - "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": 1, - "label": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nCompleted\nCancelled\nOverdue", - "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": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Has Certificate ", - "length": 0, - "no_copy": 0, - "options": "", - "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 - }, + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "fieldtype": "Attach", - "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": "Certificate", - "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 - }, + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.description", - "fieldname": "description", - "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": "Description", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actions_performed", - "fieldtype": "Text Editor", - "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": "Actions performed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, { - "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": "Asset Maintenance Log", - "permlevel": 0, - "precision": "", - "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": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "read_only": 1 } - ], - "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.457835", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-28 20:51:48.238397", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "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": 1, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index d98cc310b3b..2a5666d5064 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -7,5 +7,4 @@ import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): - def autoname(self): - self.name = self.maintenance_task + pass From d6587fa1d5dc115cf2ea09946800aba9878f9200 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 29 May 2020 12:44:09 +0530 Subject: [PATCH 339/410] fix: check if swift_number exists in bank account Signed-off-by: Chinmay D. Pai --- .../patches/v12_0/move_bank_account_swift_number_to_bank.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py index 3c9758eb84a..1ddbae6cd29 100644 --- a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py +++ b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py @@ -4,7 +4,7 @@ import frappe def execute(): frappe.reload_doc('accounts', 'doctype', 'bank', force=1) - if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account'): + if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account') and frappe.db.has_column('Bank Account', 'swift_number'): frappe.db.sql(""" UPDATE `tabBank` b, `tabBank Account` ba SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code @@ -12,4 +12,4 @@ def execute(): """) frappe.reload_doc('accounts', 'doctype', 'bank_account') - frappe.reload_doc('accounts', 'doctype', 'payment_request') \ No newline at end of file + frappe.reload_doc('accounts', 'doctype', 'payment_request') From 2186c223b9513d72b8d935410aca305d732761d9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 May 2020 21:26:49 +0530 Subject: [PATCH 340/410] fix: Migrate standard_tax_exemption_amount if field exists (#22036) --- ..._slabs_from_payroll_period_to_income_tax_slab.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index ec94cd01d1e..5ade8ca0f4c 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -14,15 +14,21 @@ def execute(): frappe.reload_doc("hr", "doctype", doctype) + standard_tax_exemption_amount_exists = frappe.db.has_column("Payroll Period", "standard_tax_exemption_amount") + + select_fields = "name, start_date, end_date" + if standard_tax_exemption_amount_exists: + select_fields = "name, start_date, end_date, standard_tax_exemption_amount" + for company in frappe.get_all("Company"): payroll_periods = frappe.db.sql(""" SELECT - name, start_date, end_date, standard_tax_exemption_amount + {0} FROM `tabPayroll Period` WHERE company=%s ORDER BY start_date DESC - """, company.name, as_dict = 1) + """.format(select_fields), company.name, as_dict = 1) for i, period in enumerate(payroll_periods): income_tax_slab = frappe.new_doc("Income Tax Slab") @@ -36,7 +42,8 @@ def execute(): income_tax_slab.effective_from = period.start_date income_tax_slab.company = company.name income_tax_slab.allow_tax_exemption = 1 - income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + if standard_tax_exemption_amount_exists: + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount income_tax_slab.flags.ignore_mandatory = True income_tax_slab.submit() From 7544160374d1b954ea62d890ff5203756fa0feaa Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 29 May 2020 21:27:24 +0530 Subject: [PATCH 341/410] fix: routing operations table is blank on pull of operations in BOM (#22039) --- erpnext/manufacturing/doctype/bom/bom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 1bdac5731ef..3253a496ed0 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -113,7 +113,13 @@ class BOM(WebsiteGenerator): self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], filters = {'parenttype': 'Routing', 'parent': self.routing}): - child = self.append('operations', d) + child = self.append('operations', { + "operation": d.operation, + "workstation": d.workstation, + "description": d.description, + "time_in_mins": d.time_in_mins, + "batch_size": d.batch_size + }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) def set_bom_material_details(self): From 40694c98aaedea789f9db4a997a46a63affeadde Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Sat, 30 May 2020 01:02:06 +0530 Subject: [PATCH 342/410] fix(HR) : Filter Leave Type based on allocation for a particular employee (#22050) * table was showing empty with just headers when no leaves allocated, fixed template code * added filters on Leave Type based on leave allocation for a particular employee and to/from dates --- erpnext/hr/doctype/leave_application/leave_application.js | 7 +++++++ .../leave_application/leave_application_dashboard.html | 7 +++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 1f50e27098c..473aae6f42f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -67,6 +67,13 @@ frappe.ui.form.on("Leave Application", { }) ); frm.dashboard.show(); + frm.set_query('leave_type', function(){ + return { + filters : [ + ['leave_type_name', 'in', Object.keys(leave_details)] + ] + } + }); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 295f3b43419..d30e3b9f9c6 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -1,5 +1,5 @@ -{% if data %} +{% if not jQuery.isEmptyObject(data) %}
    {{ __("Allocated Leaves") }}
    {{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Expired Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}
    {%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["pending_leaves"] %} {%= value["remaining_leaves"] %}
    @@ -11,7 +11,6 @@ - {% for(const [key, value] of Object.entries(data)) { %} @@ -26,6 +25,6 @@ {% } %}
    {{ __("Pending Leaves") }} {{ __("Available Leaves") }}
    -{% } else { %} +{% else %}

    No Leaves have been allocated.

    -{% } %} \ No newline at end of file +{% endif %} \ No newline at end of file From ca46bedfcb874ac5e03d44a00b996d31db336d85 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Sat, 30 May 2020 15:00:56 +0530 Subject: [PATCH 343/410] fix(ewb): remove checksum validation for TRANSIN --- erpnext/regional/india/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 732780a0a33..3085a310c41 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -615,8 +615,9 @@ def get_transport_details(data, doc): data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy') if doc.gst_transporter_id: - validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') - data.transporterId = doc.gst_transporter_id + if doc.gst_transporter_id[0:2] != "88": + validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') + data.transporterId = doc.gst_transporter_id return data From c0c455c471a8952410cd15fd882037b5f6a4c9c1 Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 31 May 2020 20:11:40 +0530 Subject: [PATCH 344/410] fix: Procurement Tracker Data Consistency --- .../procurement_tracker.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 39668795cba..88a865f0f85 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): columns = get_columns(filters) @@ -54,15 +55,16 @@ def get_columns(filters): "width": 140 }, { - "label": _("Description"), - "fieldname": "description", - "fieldtype": "Data", - "width": 200 + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 }, { "label": _("Quantity"), "fieldname": "quantity", - "fieldtype": "Int", + "fieldtype": "Float", "width": 140 }, { @@ -118,7 +120,7 @@ def get_columns(filters): }, { "label": _("Purchase Order Amount(Company Currency)"), - "fieldname": "purchase_order_amt_usd", + "fieldname": "purchase_order_amt_in_company_currency", "fieldtype": "Float", "width": 140 }, @@ -175,17 +177,17 @@ def get_data(filters): "requesting_site": po.warehouse, "requestor": po.owner, "material_request_no": po.material_request, - "description": po.description, - "quantity": po.qty, + "item_code": po.item_code, + "quantity": flt(po.qty), "unit_of_measurement": po.stock_uom, "status": po.status, "purchase_order_date": po.transaction_date, "purchase_order": po.parent, "supplier": po.supplier, - "estimated_cost": mr_record.get('amount'), - "actual_cost": pi_records.get(po.name), - "purchase_order_amt": po.amount, - "purchase_order_amt_in_company_currency": po.base_amount, + "estimated_cost": flt(mr_record.get('amount')), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, "actual_delivery_date": pr_records.get(po.name) } @@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions): SELECT par.transaction_date, par.per_ordered, + par.owner, child.name, child.parent, - child.amount + child.amount, + child.qty, + child.item_code, + child.uom, + par.status FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE par.per_ordered>=0 @@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions): procurement_record_details = dict( material_request_date=record.transaction_date, material_request_no=record.parent, - estimated_cost=record.amount + requestor=record.owner, + item_code=record.item_code, + estimated_cost=flt(record.amount), + quantity=flt(record.qty), + unit_of_measurement=record.uom, + status=record.status, + actual_cost=0, + purchase_order_amt=0, + purchase_order_amt_in_company_currency=0 ) procurement_record_against_mr.append(procurement_record_details) return mr_records, procurement_record_against_mr @@ -259,7 +274,7 @@ def get_po_entries(conditions): child.warehouse, child.material_request, child.material_request_item, - child.description, + child.item_code, child.stock_uom, child.qty, child.amount, From de03d2cba44002c281235ea3226acd2fcc8779cb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 1 Jun 2020 11:30:34 +0530 Subject: [PATCH 345/410] fix: Method in hooks for proccesing deferred revenue --- erpnext/hooks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ab161aa9f51..9d7cdc2a3b9 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -320,8 +320,7 @@ scheduler_events = { "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ - "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", - "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense", + "erpnext.accounts.deferred_revenue.process_deferred_accounting", "erpnext.hr.utils.allocate_earned_leaves", "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans" ] From 0df7f0fe9d6ed2889a94b8849964a7ebadba451a Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 1 Jun 2020 11:56:33 +0530 Subject: [PATCH 346/410] fix: Misleading Error message for Item Attribute. --- erpnext/controllers/item_variant.py | 15 +++++++++++---- .../doctype/item_attribute/item_attribute.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 29f8dd57026..50b17abbe6d 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None): else: attributes_list = attribute_values.get(attribute.lower(), []) - validate_item_attribute_value(attributes_list, attribute, value, item.name) + validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True) def validate_is_incremental(numeric_attribute, attribute, value, item): from_range = numeric_attribute.from_range @@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item): .format(attribute, from_range, to_range, increment, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) -def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): +def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True): allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) + if from_variant: + frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( + frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) + else: + msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + frappe.bold(attribute_value), frappe.bold(item)) + msg += "
    " + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) + + frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 71b998fb954..2f75bbd97c0 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -34,7 +34,7 @@ class ItemAttribute(Document): if self.numeric_values: validate_is_incremental(self, self.name, item.value, item.name) else: - validate_item_attribute_value(attributes_list, self.name, item.value, item.name) + validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False) def validate_numeric(self): if self.numeric_values: From 732f5c18674a9fde30b6e8fd5e0ae20b66a58802 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 1 Jun 2020 23:51:39 +0530 Subject: [PATCH 347/410] fixed customer count logic --- .../customer_acquisition_and_loyalty.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 88bd9c135d5..e1633ae5ed1 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -169,8 +169,7 @@ def get_customer_stats(filters, tree_view=False): customers_in = {} for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s - {company_condition} order by posting_date'''.format(company_condition=company_condition), + where docstatus=1 and posting_date <= %(to_date)s {company_condition} order by posting_date'''.format(company_condition=company_condition), filters, as_dict=1): key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') From 192d4c8810a32e1ca98e4c6eae7550e6a553f61e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 1 Jun 2020 23:58:05 +0530 Subject: [PATCH 348/410] indent to tabs instead of spaces --- .../customer_acquisition_and_loyalty.py | 309 +++++++++--------- 1 file changed, 155 insertions(+), 154 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index e1633ae5ed1..e78d0ff3a28 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -8,179 +8,180 @@ from frappe import _ from frappe.utils import cint, cstr def execute(filters=None): - common_columns = [ - { - 'label': _('New Customers'), - 'fieldname': 'new_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Repeat Customers'), - 'fieldname': 'repeat_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Total'), - 'fieldname': 'total', - 'fieldtype': 'Int', - 'default': 0, - 'width': 100 - }, - { - 'label': _('New Customer Revenue'), - 'fieldname': 'new_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Repeat Customer Revenue'), - 'fieldname': 'repeat_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Total Revenue'), - 'fieldname': 'total_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - } - ] - if filters.get('view_type') == 'Monthly': - return get_data_by_time(filters, common_columns) - else: - return get_data_by_territory(filters, common_columns) + common_columns = [ + { + 'label': _('New Customers'), + 'fieldname': 'new_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Repeat Customers'), + 'fieldname': 'repeat_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Int', + 'default': 0, + 'width': 100 + }, + { + 'label': _('New Customer Revenue'), + 'fieldname': 'new_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Repeat Customer Revenue'), + 'fieldname': 'repeat_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Total Revenue'), + 'fieldname': 'total_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + } + ] + if filters.get('view_type') == 'Monthly': + return get_data_by_time(filters, common_columns) + else: + return get_data_by_territory(filters, common_columns) def get_data_by_time(filters, common_columns): - # key yyyy-mm - columns = [ - { - 'label': _('Year'), - 'fieldname': 'year', - 'fieldtype': 'Data', - 'width': 100 - }, - { - 'label': _('Month'), - 'fieldname': 'month', - 'fieldtype': 'Data', - 'width': 100 - }, - ] - columns += common_columns + # key yyyy-mm + columns = [ + { + 'label': _('Year'), + 'fieldname': 'year', + 'fieldtype': 'Data', + 'width': 100 + }, + { + 'label': _('Month'), + 'fieldname': 'month', + 'fieldtype': 'Data', + 'width': 100 + }, + ] + columns += common_columns - customers_in = get_customer_stats(filters) + customers_in = get_customer_stats(filters) - # time series - from_year, from_month, temp = filters.get('from_date').split('-') - to_year, to_month, temp = filters.get('to_date').split('-') + # time series + from_year, from_month, temp = filters.get('from_date').split('-') + to_year, to_month, temp = filters.get('to_date').split('-') - from_year, from_month, to_year, to_month = \ - cint(from_year), cint(from_month), cint(to_year), cint(to_month) + from_year, from_month, to_year, to_month = \ + cint(from_year), cint(from_month), cint(to_year), cint(to_month) - out = [] - for year in range(from_year, to_year+1): - for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): - key = '{year}-{month:02d}'.format(year=year, month=month) - data = customers_in.get(key) - new = data['new'] if data else [0, 0.0] - repeat = data['repeat'] if data else [0, 0.0] - out.append({ - 'year': cstr(year), - 'month': calendar.month_name[month], - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1] - }) - return columns, out + out = [] + for year in range(from_year, to_year+1): + for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): + key = '{year}-{month:02d}'.format(year=year, month=month) + data = customers_in.get(key) + new = data['new'] if data else [0, 0.0] + repeat = data['repeat'] if data else [0, 0.0] + out.append({ + 'year': cstr(year), + 'month': calendar.month_name[month], + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1] + }) + return columns, out def get_data_by_territory(filters, common_columns): - columns = [{ - 'label': 'Territory', - 'fieldname': 'territory', - 'fieldtype': 'Link', - 'options': 'Territory', - 'width': 150 - }] - columns += common_columns + columns = [{ + 'label': 'Territory', + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 150 + }] + columns += common_columns - customers_in = get_customer_stats(filters, tree_view=True) + customers_in = get_customer_stats(filters, tree_view=True) - territory_dict = {} - for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): - territory_dict.update({ - t.name: { - 'parent': t.parent_territory, - 'is_group': t.is_group - } - }) + territory_dict = {} + for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): + territory_dict.update({ + t.name: { + 'parent': t.parent_territory, + 'is_group': t.is_group + } + }) - depth_map = frappe._dict() - for name, info in territory_dict.items(): - default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 - depth_map.setdefault(name, default) + depth_map = frappe._dict() + for name, info in territory_dict.items(): + default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 + depth_map.setdefault(name, default) - data = [] - for name, indent in depth_map.items(): - condition = customers_in.get(name) - new = customers_in[name]['new'] if condition else [0, 0.0] - repeat = customers_in[name]['repeat'] if condition else [0, 0.0] - temp = { - 'territory': name, - 'parent_territory': territory_dict[name]['parent'], - 'indent': indent, - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1], - 'bold': 0 if indent else 1 - } - data.append(temp) + data = [] + for name, indent in depth_map.items(): + condition = customers_in.get(name) + new = customers_in[name]['new'] if condition else [0, 0.0] + repeat = customers_in[name]['repeat'] if condition else [0, 0.0] + temp = { + 'territory': name, + 'parent_territory': territory_dict[name]['parent'], + 'indent': indent, + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1], + 'bold': 0 if indent else 1 + } + data.append(temp) - loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) + loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) - for ld in loop_data: - if ld['parent_territory']: - parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] - for key in parent_data.keys(): - if key not in ['indent', 'territory', 'parent_territory', 'bold']: - parent_data[key] += ld[key] + for ld in loop_data: + if ld['parent_territory']: + parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] + for key in parent_data.keys(): + if key not in ['indent', 'territory', 'parent_territory', 'bold']: + parent_data[key] += ld[key] - return columns, data, None, None, None, 1 + return columns, data, None, None, None, 1 def get_customer_stats(filters, tree_view=False): - """ Calculates number of new and repeated customers. """ - company_condition = '' - if filters.get('company'): - company_condition = ' and company=%(company)s' + """ Calculates number of new and repeated customers. """ + company_condition = '' + if filters.get('company'): + company_condition = ' and company=%(company)s' - customers = [] - customers_in = {} + customers = [] + customers_in = {} - for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s {company_condition} order by posting_date'''.format(company_condition=company_condition), - filters, as_dict=1): + for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` + where docstatus=1 and posting_date <= %(to_date)s + {company_condition} order by posting_date'''.format(company_condition=company_condition), + filters, as_dict=1): - key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') - customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) + key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') + customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - if not si.customer in customers: - customers_in[key]['new'][0] += 1 - customers_in[key]['new'][1] += si.base_grand_total - customers.append(si.customer) - else: - customers_in[key]['repeat'][0] += 1 - customers_in[key]['repeat'][1] += si.base_grand_total + if not si.customer in customers: + customers_in[key]['new'][0] += 1 + customers_in[key]['new'][1] += si.base_grand_total + customers.append(si.customer) + else: + customers_in[key]['repeat'][0] += 1 + customers_in[key]['repeat'][1] += si.base_grand_total - return customers_in + return customers_in From 60f3a2b8e7ce5a8b84f565342e1397b13b2bc095 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 2 Jun 2020 18:26:43 +0530 Subject: [PATCH 349/410] fix: Add total debit in Journal Entry list view --- erpnext/accounts/doctype/journal_entry/journal_entry.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 9d5063929fd..af2aa65e6b2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -191,6 +191,7 @@ { "fieldname": "total_debit", "fieldtype": "Currency", + "in_list_view": 1, "label": "Total Debit", "no_copy": 1, "oldfieldname": "total_debit", @@ -252,7 +253,6 @@ "fieldname": "total_amount", "fieldtype": "Currency", "hidden": 1, - "in_list_view": 1, "label": "Total Amount", "no_copy": 1, "options": "total_amount_currency", @@ -503,7 +503,7 @@ "idx": 176, "is_submittable": 1, "links": [], - "modified": "2020-04-29 10:55:28.240916", + "modified": "2020-06-02 18:15:46.955697", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", From 789df326837b85210f769978dfdc6e7e5004eaf1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 2 Jun 2020 13:48:22 +0000 Subject: [PATCH 350/410] feat: verify signature on webhook (#21872) --- .../doctype/membership/membership.py | 13 +++++++++- .../membership_settings.js | 26 +++++++++++++++++-- .../membership_settings.json | 11 ++++++-- .../membership_settings.py | 18 ++++++++++++- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index df19995a1c5..ac5078d45cc 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -64,9 +64,21 @@ def get_member_based_on_subscription(subscription_id, email): }, order_by="creation desc") return frappe.get_doc("Member", members[0]['name']) +def verify_signature(data): + signature = frappe.request.headers.get('X-Razorpay-Signature') + + settings = frappe.get_doc("Membership Settings") + key = settings.get_webhook_secret() + + controller = frappe.get_doc("Razorpay Settings") + + controller.verify_signature(data, signature, key) + + @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): data = frappe.request.get_data() + verify_signature(data): if isinstance(data, six.string_types): data = json.loads(data) @@ -113,7 +125,6 @@ def trigger_razorpay_subscription(*args, **kwargs): return True - def notify_failure(log): try: content = """Dear System Manager, diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.js b/erpnext/non_profit/doctype/membership_settings/membership_settings.js index c01a0b23d5d..8c0e3a4fa76 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.js @@ -1,8 +1,30 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Membership Settings', { +frappe.ui.form.on("Membership Settings", { refresh: function(frm) { + if (frm.doc.webhook_secret) { + frm.add_custom_button(__("Revoke "), () => { + frm.call("revoke_key").then(() => { + frm.refresh(); + }) + }); + } + frm.trigger("add_generate_button"); + }, - } + add_generate_button: function(frm) { + let label; + + if (frm.doc.webhook_secret) { + label = __("Regenerate Webhook Secret"); + } else { + label = __("Generate Webhook Secret"); + } + frm.add_custom_button(label, () => { + frm.call("generate_webhook_key").then(() => { + frm.refresh(); + }); + }); + }, }); diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index 56b8eac4b12..52b9d01088b 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -8,7 +8,8 @@ "enable_razorpay", "razorpay_settings_section", "billing_cycle", - "billing_frequency" + "billing_frequency", + "webhook_secret" ], "fields": [ { @@ -34,11 +35,17 @@ "fieldname": "billing_frequency", "fieldtype": "Int", "label": "Billing Frequency" + }, + { + "fieldname": "webhook_secret", + "fieldtype": "Password", + "label": "Webhook Secret", + "read_only": 1 } ], "issingle": 1, "links": [], - "modified": "2020-04-07 18:42:51.496807", + "modified": "2020-05-22 12:38:27.103759", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.py b/erpnext/non_profit/doctype/membership_settings/membership_settings.py index 2b8e37f2a65..f3b2eee6f97 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.py +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.py @@ -4,11 +4,27 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.integrations.utils import get_payment_gateway_controller from frappe.model.document import Document class MembershipSettings(Document): - pass + def generate_webhook_key(self): + key = frappe.generate_hash(length=20) + self.webhook_secret = key + self.save() + + frappe.msgprint( + _("Here is your webhook secret, this will be shown to you only once.") + "

    " + key, + _("Webhook Secret") + ); + + def revoke_key(self): + self.webhook_secret = None; + self.save() + + def get_webhook_secret(self): + return self.get_password(fieldname="webhook_secret", raise_exception=False) @frappe.whitelist() def get_plans_for_membership(*args, **kwargs): From f460c1796140b64cbdcdab121b64fd26b2d9b46a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 2 Jun 2020 16:18:23 +0000 Subject: [PATCH 351/410] fix: syntax error (#22082) --- erpnext/non_profit/doctype/membership/membership.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index ac5078d45cc..4b932425b28 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -78,7 +78,7 @@ def verify_signature(data): @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): data = frappe.request.get_data() - verify_signature(data): + verify_signature(data) if isinstance(data, six.string_types): data = json.loads(data) From 9276d164201a52ed7c699786a0181cb68d85cf5d Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Wed, 3 Jun 2020 11:14:49 +0530 Subject: [PATCH 352/410] fix(stock): able to create purchase invoice from purchase receipt dashboard able to create purchase invoice from purchase receipt dashboard --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e9568eeacc0..50c18f62824 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", { frm.custom_make_buttons = { 'Stock Entry': 'Return', - 'Purchase Invoice': 'Invoice' + 'Purchase Invoice': 'Purchase Invoice' }; frm.set_query("expense_account", "items", function() { From b84dd9b5f58746a8f9e4928ee2a6bb21c7c9ef28 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 3 Jun 2020 13:24:18 +0530 Subject: [PATCH 353/410] fix: dashboard chart does not exist on selling page --- erpnext/selling/desk_page/selling/selling.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index c32a7c8481d..9ec634354d3 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -29,7 +29,7 @@ "category": "Modules", "charts": [ { - "chart_name": "Income", + "chart_name": "Incoming Bills (Purchase Invoice)", "label": "Income" } ], @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-05-28 13:46:08.314240", + "modified": "2020-06-03 13:23:24.861706", "modified_by": "Administrator", "module": "Selling", "name": "Selling", From 0c7224940391ab3c512ce742048ab29a2a6271a9 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 14:32:07 +0530 Subject: [PATCH 354/410] fix: Apply shipping rule without address too --- erpnext/accounts/doctype/shipping_rule/shipping_rule.py | 4 +++- erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index b2638c78279..d32a348741e 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -45,7 +45,9 @@ class ShippingRule(Document): shipping_amount = 0.0 by_value = False - self.validate_countries(doc) + if doc.get_shipping_address(): + # validate country only if there is address + self.validate_countries(doc) if self.calculate_based_on == 'Net Total': value = doc.base_net_total diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 524a95804fd..94216680020 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -917,7 +917,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { + if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", From 6f7e9d2904bcc11ebf15e166232d4aa76b599b16 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 17:13:58 +0530 Subject: [PATCH 355/410] fix: '>=' not supported between instances of 'str' and 'int' --- erpnext/stock/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index f21dc3f8b03..11e758fce32 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -230,12 +230,12 @@ def get_valuation_method(item_code): def get_fifo_rate(previous_stock_queue, qty): """get FIFO (average) Rate from Queue""" - if qty >= 0: + if flt(qty) >= 0: total = sum(f[0] for f in previous_stock_queue) return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0 else: available_qty_for_outgoing, outgoing_cost = 0, 0 - qty_to_pop = abs(qty) + qty_to_pop = abs(flt(qty)) while qty_to_pop and previous_stock_queue: batch = previous_stock_queue[0] if 0 < batch[0] <= qty_to_pop: From 7135a75e5e28a5329b709174688761f78c307dff Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 21:51:21 +0530 Subject: [PATCH 356/410] fix: Error when no data is present in AR/AP reeport --- .../report/accounts_receivable/accounts_receivable.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index d40e58b5287..66aa18058b6 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -169,9 +169,11 @@ class ReceivablePayableReport(object): def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) - self.data.append(sub_total_row) - self.data.append({}) - self.update_sub_total_row(sub_total_row, 'Total') + + if sub_total_row: + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') def get_voucher_balance(self, gle): if self.filters.get("sales_person"): @@ -232,7 +234,8 @@ class ReceivablePayableReport(object): if self.filters.get('group_by_party'): self.append_subtotal_row(self.previous_party) - self.data.append(self.total_row_map.get('Total')) + if self.data: + self.data.append(self.total_row_map.get('Total')) def append_row(self, row): self.allocate_future_payments(row) From 225802e3a0b09fc074c9bc25950d108d2808d1da Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 4 Jun 2020 14:11:18 +0530 Subject: [PATCH 357/410] fix: problem during assigning --- erpnext/hr/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index cd125108c61..8d95924681a 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -73,11 +73,11 @@ class EmployeeBoardingController(Document): def assign_task_to_users(self, task, users): for user in users: args = { - 'assign_to' : user, - 'doctype' : task.doctype, - 'name' : task.name, - 'description' : task.description or task.subject, - 'notify': self.notify_users_by_email + 'assign_to': [user], + 'doctype': task.doctype, + 'name': task.name, + 'description': task.description or task.subject, + 'notify': self.notify_users_by_email } assign_to.add(args) From 60c18550148ee7fe4972113eb5e1d3899fa839e1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:28:34 +0530 Subject: [PATCH 358/410] fix: import supplier invoice not working --- .../doctype/import_supplier_invoice/import_supplier_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 6b9567c0e57..31a7545a0df 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document): "buying_price_list": self.default_buying_price_list } - if not invoices_args.get("invoice_no", ''): return + if not invoices_args.get("bill_no", ''): + frappe.throw(_("Numero has not set in the XML file")) supp_dict = get_supplier_details(file_content) invoices_args["destination_code"] = get_destination_code_from_file(file_content) From 404ed88c0ca34aae06cb6e7b91a8442ec494440b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:46:50 +0530 Subject: [PATCH 359/410] fix: routing operations not added sequentially in the BOM --- erpnext/manufacturing/doctype/bom/bom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 3253a496ed0..2543eec53e4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -112,13 +112,14 @@ class BOM(WebsiteGenerator): if self.routing: self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], - filters = {'parenttype': 'Routing', 'parent': self.routing}): + filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"): child = self.append('operations', { "operation": d.operation, "workstation": d.workstation, "description": d.description, "time_in_mins": d.time_in_mins, - "batch_size": d.batch_size + "batch_size": d.batch_size, + "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) From 94582a89c7a25af17d65d9b03ad76519ba42eeba Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 4 Jun 2020 17:50:47 +0530 Subject: [PATCH 360/410] fix: Wrong filters --- erpnext/hr/doctype/leave_application/leave_application.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f2968bcd889..4499fa6016c 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -131,8 +131,6 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" - print("-------->>>", status) - # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -442,6 +440,7 @@ def get_leave_details(employee, date): total_allocated_leaves = frappe.db.get_value('Leave Allocation', { 'from_date': ('<=', date), 'to_date': ('>=', date), + 'employee': employee, 'leave_type': allocation.leave_type, }, 'SUM(total_leaves_allocated)') or 0 From 6f87f97efe521526d3eac512305c7c6b62bb3b9e Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:27:07 +0200 Subject: [PATCH 361/410] fix: Wrong Ordered-Status Indicator for Material Request Items (#22118) The indicator displaying if a material request item has been ordered or not (green/orange) used the wrong quantity field for determining the status. The qty field used in the code is not in stock uom while the ordered_qty field is. Now, the stock_qty field is used for correct comparison with ordered_qty. --- erpnext/stock/doctype/material_request/material_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 3562181e25f..3a8deb6d254 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -18,7 +18,7 @@ frappe.ui.form.on('Material Request', { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); + function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; }); frm.set_query("item_code", "items", function() { return { From 5a82276389851d0ecf701d2219c8d10d0fe15433 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jun 2020 13:56:20 +0530 Subject: [PATCH 362/410] fix: 'ForecastingReport' object has no attribute 'total_demand' --- .../exponential_smoothing_forecasting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index cac80677297..2ca9f1694b3 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -217,6 +217,8 @@ class ForecastingReport(ExponentialSmoothingForecast): } def get_summary_data(self): + if not self.data: return + return [ { "value": sum(self.total_demand), From 328a55749d8666028544e098cd280865d946f861 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 5 Jun 2020 16:46:57 +0530 Subject: [PATCH 363/410] fix(HR): Fix half day error when only 1 day is selected (#22122) * fix half day error when only 1 day is selected * adding validation fix for half day on serverside --- erpnext/hr/doctype/leave_application/leave_application.js | 3 +++ erpnext/hr/doctype/leave_application/leave_application.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 473aae6f42f..15ce468c138 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -38,6 +38,9 @@ frappe.ui.form.on("Leave Application", { }, validate: function(frm) { + if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ + frm.doc.half_day_date = frm.doc.from_date; + } frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f2968bcd889..5eb84d770a1 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -33,6 +33,7 @@ class LeaveApplication(Document): self.validate_block_days() self.validate_salary_processed_days() self.validate_attendance() + self.set_half_day_date() if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'): self.validate_optional_leave() self.validate_applicable_after() @@ -292,6 +293,10 @@ class LeaveApplication(Document): frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday) day = add_days(day, 1) + def set_half_day_date(self): + if self.from_date == self.to_date and self.half_day == 1: + self.half_day_date = self.from_date + def notify_employee(self): employee = frappe.get_doc("Employee", self.employee) if not employee.user_id: From 3375a2e85a5d50cdb9e2112e7a55168fdd2ef00a Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Sat, 6 Jun 2020 21:08:49 +0530 Subject: [PATCH 364/410] ci: trigger docker build on release (#22128) --- .github/workflows/docker-release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/docker-release.yml diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 00000000000..d36b11553ce --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,14 @@ +name: Trigger Docker build on release +on: + release: + types: [created] +jobs: + curl: + runs-on: ubuntu-latest + container: + image: alpine:latest + steps: + - name: curl + run: | + apk add curl bash + curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.org/repo/frappe%2Ffrappe_docker/requests From 57f9d43c43f27444054ae8e32ecf52fc0d04a295 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 00:01:20 +0530 Subject: [PATCH 365/410] fix: Cancelled entries in tds payable monthly report --- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 4ac0f656119..a9fb237a048 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -111,7 +111,7 @@ def get_gle_map(filters): # {"purchase_invoice": list of dict of all gle created for this invoice} gle_map = {} gle = frappe.db.get_all('GL Entry',\ - {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]}, + {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0}, ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) for d in gle: From 303112816771639a7808663170dac497b4f8e2d1 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 7 Jun 2020 18:02:23 +0530 Subject: [PATCH 366/410] fix: fetch tax lines within the shipping lines (#22138) --- .../connectors/shopify_connection.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 7046038fb24..d59f9092983 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings): return taxes def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): + """Shipping lines represents the shipping details, + each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) + for tax in shipping_charge.get("tax_lines"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(tax), + "description": tax["title"], + "tax_amount": tax["price"], + "cost_center": shopify_settings.cost_center + }) return taxes From e5ac8cc430e4e3165b62c550dcf5e3eef55cf8b4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 21:18:47 +0530 Subject: [PATCH 367/410] fix: Precision in loan amount calculation --- .../loan_management/desk_page/loan/loan.json | 5 +-- .../loan_interest_accrual.py | 30 +++++++------- .../doctype/loan_repayment/loan_repayment.py | 40 +++++++++++-------- .../doctype/loan_type/loan_type.json | 3 +- .../loan_security_status.py | 13 +++--- 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index d79860a3520..48193b0a0d8 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"doctype\": \"Loan Repayment\",\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"doctype\": \"Loan Security Pledge\",\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -34,11 +34,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-05-28 13:37:42.017709", + "modified": "2020-06-07 19:42:14.947902", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 094b9c698c7..659173557be 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -176,21 +176,23 @@ def get_term_loans(date, term_loan=None, loan_type=None): return term_loans def make_loan_interest_accrual_entry(args): - loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") - loan_interest_accrual.loan = args.loan - loan_interest_accrual.applicant_type = args.applicant_type - loan_interest_accrual.applicant = args.applicant - loan_interest_accrual.interest_income_account = args.interest_income_account - loan_interest_accrual.loan_account = args.loan_account - loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2) - loan_interest_accrual.interest_amount = flt(args.interest_amount, 2) - loan_interest_accrual.posting_date = args.posting_date or nowdate() - loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest - loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name - loan_interest_accrual.payable_principal_amount = args.payable_principal + precision = cint(frappe.db.get_default("currency_precision")) or 2 - loan_interest_accrual.save() - loan_interest_accrual.submit() + loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") + loan_interest_accrual.loan = args.loan + loan_interest_accrual.applicant_type = args.applicant_type + loan_interest_accrual.applicant = args.applicant + loan_interest_accrual.interest_income_account = args.interest_income_account + loan_interest_accrual.loan_account = args.loan_account + loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision) + loan_interest_accrual.interest_amount = flt(args.interest_amount, precision) + loan_interest_accrual.posting_date = args.posting_date or nowdate() + loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest + loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name + loan_interest_accrual.payable_principal_amount = args.payable_principal + + loan_interest_accrual.save() + loan_interest_accrual.submit() def get_no_of_days_for_interest_accural(loan, posting_date): diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 2ab668a0e11..c28994e280b 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import json from frappe import _ -from frappe.utils import flt, getdate +from frappe.utils import flt, getdate, cint from six import iteritems from frappe.model.document import Document from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime @@ -29,8 +29,11 @@ class LoanRepayment(AccountsController): def on_cancel(self): self.mark_as_unpaid() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self, amounts): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.posting_date: self.posting_date = get_datetime() @@ -38,24 +41,26 @@ class LoanRepayment(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) if not self.interest_payable: - self.interest_payable = flt(amounts['interest_amount'], 2) + self.interest_payable = flt(amounts['interest_amount'], precision) if not self.penalty_amount: - self.penalty_amount = flt(amounts['penalty_amount'], 2) + self.penalty_amount = flt(amounts['penalty_amount'], precision) if not self.pending_principal_amount: - self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2) + self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision) if not self.payable_principal_amount and self.is_term_loan: - self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2) + self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision) if not self.payable_amount: - self.payable_amount = flt(amounts['payable_amount'], 2) + self.payable_amount = flt(amounts['payable_amount'], precision) if amounts.get('due_date'): self.due_date = amounts.get('due_date') def validate_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.amount_paid: frappe.throw(_("Amount paid cannot be zero")) @@ -63,11 +68,13 @@ class LoanRepayment(AccountsController): msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount) frappe.throw(msg) - if self.payment_type == "Loan Closure" and flt(self.amount_paid, 2) < flt(self.payable_amount, 2): + if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision): msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount) frappe.throw(msg) def update_paid_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + loan = frappe.get_doc("Loan", self.against_loan) for payment in self.repayment_details: @@ -75,9 +82,9 @@ class LoanRepayment(AccountsController): SET paid_principal_amount = `paid_principal_amount` + %s, paid_interest_amount = `paid_interest_amount` + %s WHERE name = %s""", - (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) + (flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual)) - if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2): + if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision): if loan.is_secured_loan: frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested") else: @@ -253,6 +260,7 @@ def get_accrued_interest_entries(against_loan): # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable def get_amounts(amounts, against_loan, posting_date, payment_type): + precision = cint(frappe.db.get_default("currency_precision")) or 2 against_loan_doc = frappe.get_doc("Loan", against_loan) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) @@ -282,8 +290,8 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): payable_principal_amount += entry.payable_principal_amount pending_accrual_entries.setdefault(entry.name, { - 'interest_amount': flt(entry.interest_amount), - 'payable_principal_amount': flt(entry.payable_principal_amount) + 'interest_amount': flt(entry.interest_amount, precision), + 'payable_principal_amount': flt(entry.payable_principal_amount, precision) }) if not final_due_date: @@ -301,11 +309,11 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365 total_pending_interest += (pending_days * per_day_interest) - amounts["pending_principal_amount"] = pending_principal_amount - amounts["payable_principal_amount"] = payable_principal_amount - amounts["interest_amount"] = total_pending_interest - amounts["penalty_amount"] = penalty_amount - amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount + amounts["pending_principal_amount"] = flt(pending_principal_amount, precision) + amounts["payable_principal_amount"] = flt(payable_principal_amount, precision) + amounts["interest_amount"] = flt(total_pending_interest, precision) + amounts["penalty_amount"] = flt(penalty_amount, precision) + amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision) amounts["pending_accrual_entries"] = pending_accrual_entries if final_due_date: diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index 51c5cb98a63..1dd3710cd2a 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -41,6 +41,7 @@ "options": "Company:company:default_currency" }, { + "default": "0", "fieldname": "rate_of_interest", "fieldtype": "Percent", "label": "Rate of Interest (%) Yearly", @@ -143,7 +144,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-15 00:24:43.259963", + "modified": "2020-06-07 18:55:59.346292", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.py b/erpnext/loan_management/report/loan_security_status/loan_security_status.py index ea6a2ee6452..19518554759 100644 --- a/erpnext/loan_management/report/loan_security_status/loan_security_status.py +++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.py @@ -76,7 +76,8 @@ def get_columns(filters): "fieldtype": "Link", "fieldname": "currency", "options": "Currency", - "width": 50 + "width": 50, + "hidden": 1 } ] @@ -84,17 +85,13 @@ def get_columns(filters): def get_data(filters): - loan_security_price_map = frappe._dict(frappe.get_all("Loan Security", - fields=["name", "loan_security_price"], as_list=1 - )) - data = [] conditions = get_conditions(filters) loan_security_pledges = frappe.db.sql(""" SELECT p.name, p.applicant, p.loan, p.status, p.pledge_time, - c.loan_security, c.qty + c.loan_security, c.qty, c.loan_security_price, c.amount FROM `tabLoan Security Pledge` p, `tabPledge` c WHERE @@ -115,8 +112,8 @@ def get_data(filters): row["pledge_time"] = pledge.pledge_time row["loan_security"] = pledge.loan_security row["qty"] = pledge.qty - row["loan_security_price"] = loan_security_price_map.get(pledge.loan_security) - row["loan_security_value"] = row["loan_security_price"] * pledge.qty + row["loan_security_price"] = pledge.loan_security_price + row["loan_security_value"] = pledge.amount row["currency"] = default_currency data.append(row) From 0bc163c230c8b5095bddf200300a3b18a3d421d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 21:23:55 +0530 Subject: [PATCH 368/410] fix: Ignore GL Entry on cancel --- .../doctype/loan_disbursement/loan_disbursement.py | 1 + .../doctype/loan_interest_accrual/loan_interest_accrual.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index c9e36a84ddb..d44088bee74 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -27,6 +27,7 @@ class LoanDisbursement(AccountsController): def on_cancel(self): self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self): if not self.disbursement_date: diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 659173557be..e6ceb551850 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -31,6 +31,7 @@ class LoanInterestAccrual(AccountsController): self.update_is_accrued() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def update_is_accrued(self): frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0) From e9ff6d3d52a0a1320bd2d3b0934e6280393b3a5c Mon Sep 17 00:00:00 2001 From: Karthikeyan S Date: Mon, 8 Jun 2020 12:20:21 +0530 Subject: [PATCH 369/410] feat(Cost Center): Distributed Cost Center (#21531) * feat: Distributed Cost Center Squashed commit of the following: commit 274236eb92dfddfd015583ee1e6b5e1937366d28 Author: Vignesh Date: Mon Mar 23 18:32:26 2020 +0530 Fix: Indentation commit beae0b9849f333dd1d9f29c27192ea7de2e4e809 Author: Vignesh Date: Mon Mar 23 18:26:50 2020 +0530 The validations are added to distributed cost center. commit 3f66943adacb5797338ef3510251bf6633be3dba Author: Vignesh Date: Thu Mar 19 22:17:25 2020 +0530 The distributed cost centers are shown with the percentage allocation in the reports commit 1ac11df522096fa187d2f5863b4c970e18b37fac Author: Vignesh Date: Thu Mar 19 13:36:04 2020 +0530 The child table distributed cost center are added to cost center doctype and then validate percentage allocation. * fix(Distributed Cost Center): validation and filters * fix(Distributed Cost Center): financial statement query * fix(Distributed Cost Center): add test cases Co-authored-by: Vignesh S * fix(Distributed Cost Center): budget variance, general ledger, profitability analysis reports. * Merge branch 'develop' into develop-distributed-cost-center * fix(Distributed Cost Center): Added DCC reflection in actual amount column of Budget Variance Report. * Update creation field in general ledger report Co-authored-by: Nabin Hait * creation field updated in gl_entries query Co-authored-by: Nabin Hait Co-authored-by: KaviyaPeriyasamy Co-authored-by: Vignesh S Co-authored-by: KaviyaPeriyasamy Co-authored-by: Kaviya Periyasamy <36359901+KaviyaPeriyasamy@users.noreply.github.com> Co-authored-by: Nabin Hait --- .../doctype/cost_center/cost_center.js | 13 +++- .../doctype/cost_center/cost_center.json | 21 +++++ .../doctype/cost_center/cost_center.py | 36 ++++++++- .../doctype/cost_center/test_cost_center.py | 27 +++++++ .../distributed_cost_center/__init__.py | 0 .../distributed_cost_center.json | 40 ++++++++++ .../distributed_cost_center.py | 10 +++ .../budget_variance_report.py | 77 ++++++++++++------- .../accounts/report/financial_statements.py | 34 +++++++- .../report/general_ledger/general_ledger.py | 39 +++++++++- .../profitability_analysis.py | 14 ++++ 11 files changed, 275 insertions(+), 36 deletions(-) create mode 100644 erpnext/accounts/doctype/distributed_cost_center/__init__.py create mode 100644 erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json create mode 100644 erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 9e2f6eed3b6..f341f782078 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -14,7 +14,18 @@ frappe.ui.form.on('Cost Center', { is_group: 1 } } - }) + }); + + frm.set_query("cost_center", "distributed_cost_center", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + enable_distributed_cost_center: 0, + name: ['!=', frm.doc.name] + } + }; + }); }, refresh: function(frm) { if (!frm.is_new()) { diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5013c92a327..c9bbbabe798 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -16,6 +16,9 @@ "cb0", "is_group", "disabled", + "section_break_9", + "enable_distributed_cost_center", + "distributed_cost_center", "lft", "rgt", "old_parent" @@ -119,6 +122,24 @@ "fieldname": "disabled", "fieldtype": "Check", "label": "Disabled" + }, + { + "default": "0", + "fieldname": "enable_distributed_cost_center", + "fieldtype": "Check", + "label": "Enable Distributed Cost Center" + }, + { + "depends_on": "eval:doc.is_group==0", + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "depends_on": "enable_distributed_cost_center", + "fieldname": "distributed_cost_center", + "fieldtype": "Table", + "label": "Distributed Cost Center", + "options": "Distributed Cost Center" } ], "icon": "fa fa-money", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index 0294e78111c..12094d4f989 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -19,6 +19,24 @@ class CostCenter(NestedSet): def validate(self): self.validate_mandatory() self.validate_parent_cost_center() + self.validate_distributed_cost_center() + + def validate_distributed_cost_center(self): + if cint(self.enable_distributed_cost_center): + if not self.distributed_cost_center: + frappe.throw(_("Please enter distributed cost center")) + if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100: + frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100")) + if not self.get('__islocal'): + if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \ + and self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center")) + if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False): + frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center")) + if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)): + frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table.")) + else: + self.distributed_cost_center = [] def validate_mandatory(self): if self.cost_center_name != self.company and not self.parent_cost_center: @@ -43,12 +61,15 @@ class CostCenter(NestedSet): return 1 def convert_ledger_to_group(self): + if cint(self.enable_distributed_cost_center): + frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group")) + if self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group")) if self.check_gle_exists(): frappe.throw(_("Cost Center with existing transactions can not be converted to group")) - else: - self.is_group = 1 - self.save() - return 1 + self.is_group = 1 + self.save() + return 1 def check_gle_exists(self): return frappe.db.get_value("GL Entry", {"cost_center": self.name}) @@ -57,6 +78,9 @@ class CostCenter(NestedSet): return frappe.db.sql("select name from `tabCost Center` where \ parent_cost_center = %s and docstatus != 2", self.name) + def check_if_part_of_distributed_cost_center(self): + return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name}) + def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided from erpnext.setup.doctype.company.company import get_name_with_abbr @@ -100,3 +124,7 @@ def get_name_with_number(new_account, account_number): if account_number and not new_account[0].isdigit(): new_account = account_number + " - " + new_account return new_account + +def check_if_distributed_cost_center_enabled(cost_center_list): + value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1) + return next((True for x in value_list if x[0]), False) \ No newline at end of file diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index 8f23d906760..b5fc7e3b497 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -22,6 +22,33 @@ class TestCostCenter(unittest.TestCase): self.assertRaises(frappe.ValidationError, cost_center.save) + def test_validate_distributed_cost_center(self): + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}): + frappe.get_doc(test_records[0]).insert() + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}): + frappe.get_doc(test_records[1]).insert() + + invalid_distributed_cost_center = frappe.get_doc({ + "company": "_Test Company", + "cost_center_name": "_Test Distributed Cost Center", + "doctype": "Cost Center", + "is_group": 0, + "parent_cost_center": "_Test Company - _TC", + "enable_distributed_cost_center": 1, + "distributed_cost_center": [{ + "cost_center": "_Test Cost Center - _TC", + "percentage_allocation": 40 + }, { + "cost_center": "_Test Cost Center 2 - _TC", + "percentage_allocation": 50 + } + ] + }) + + self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save) + def create_cost_center(**args): args = frappe._dict(args) if args.cost_center_name: diff --git a/erpnext/accounts/doctype/distributed_cost_center/__init__.py b/erpnext/accounts/doctype/distributed_cost_center/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json new file mode 100644 index 00000000000..45b0e2df9ba --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2020-03-19 12:34:01.500390", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center", + "percentage_allocation" + ], + "fields": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "fieldname": "percentage_allocation", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Percentage Allocation", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-19 12:54:43.674655", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Distributed Cost Center", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py new file mode 100644 index 00000000000..48c589f0c0f --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DistributedCostCenter(Document): + pass diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 05dc2826611..9c9ada871c8 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -29,37 +29,60 @@ def execute(filters=None): for dimension in dimensions: dimension_items = cam_map.get(dimension) if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value - - period_data[0] += last_total - - if filters.get("show_cumulative"): - last_total = period_data[0] - period_data[1] - - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly": - row += totals - data.append(row) + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0) + else: + DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(dimension)s + AND parent NOT IN %(dimension)s + GROUP BY parent''',{'dimension':[dimension]}) + if DCC_allocation: + filters['budget_against_filter'] = [DCC_allocation[0][0]] + cam_map = get_dimension_account_month_map(filters) + dimension_items = cam_map.get(DCC_allocation[0][0]) + if dimension_items: + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1]) chart = get_chart_data(filters, columns, data) return columns, data, None, chart +def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation): + + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate(["target", "actual", "variance"]): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value + + period_data[0] += last_total + + if DCC_allocation: + period_data[0] = period_data[0]*(DCC_allocation/100) + period_data[1] = period_data[1]*(DCC_allocation/100) + + if(filters.get("show_cumulative")): + last_total = period_data[0] - period_data[1] + + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly" : + row += totals + data.append(row) + + return data + + def get_columns(filters): columns = [ { @@ -366,7 +389,7 @@ def get_chart_data(filters, columns, data): budget_values[i] += values[index] actual_values[i] += values[index+1] index += 3 - + return { 'data': { 'labels': labels, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 4a35a668658..0339e4920a8 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -387,11 +387,41 @@ def set_gl_entries_by_account( key: value }) + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + distributed_cost_center_query = """ + UNION ALL + SELECT posting_date, + account, + debit*(DCC_allocation.percentage_allocation/100) as debit, + credit*(DCC_allocation.percentage_allocation/100) as credit, + is_opening, + fiscal_year, + debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency, + account_currency + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {additional_conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s - order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec + {distributed_cost_center_query} + order by account, posting_date""".format( + additional_conditions=additional_conditions, + distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec if filters and filters.get('presentation_currency'): convert_to_presentation_currency(gl_entries, get_currency(filters)) @@ -489,4 +519,4 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None): "width": 150 }) - return columns + return columns \ No newline at end of file diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f83a2595f6a..fcd36e4e6e2 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -128,18 +128,53 @@ def get_gl_entries(filters): filters['company_fb'] = frappe.db.get_value("Company", filters.get("company"), 'default_finance_book') + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """ + + distributed_cost_center_query = """ + UNION ALL + SELECT name as gl_entry, + posting_date, + account, + party_type, + party, + voucher_type, + voucher_no, + cost_center, project, + against_voucher_type, + against_voucher, + account_currency, + remarks, against, + is_opening, `tabGL Entry`.creation {select_fields_with_percentage} + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql( """ select name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening {select_fields} + remarks, against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} + {distributed_cost_center_query} {order_by_statement} """.format( - select_fields=select_fields, conditions=get_conditions(filters), + select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query, order_by_statement=order_by_statement ), filters, as_dict=1) diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index 6e9b31f2f6d..60e675f2f1f 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -105,6 +105,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name): def prepare_data(accounts, filters, total_row, parent_children_map, based_on): data = [] + new_accounts = accounts company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") for d in accounts: @@ -118,6 +119,19 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): "currency": company_currency, "based_on": based_on } + if based_on == 'cost_center': + cost_center_doc = frappe.get_doc("Cost Center",d.name) + if not cost_center_doc.enable_distributed_cost_center: + DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent""",{'cost_center': [d.name]}) + if DCC_allocation: + for account in new_accounts: + if account['name'] == DCC_allocation[0][0]: + for value in value_fields: + d[value] += account[value]*(DCC_allocation[0][1]/100) for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) From f5b9deea5b718b9b87b77e5681dfc98a226c2d6c Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 8 Jun 2020 13:02:00 +0530 Subject: [PATCH 370/410] fix: Finished Product Valuation at Repack --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 18d68539daa..5fbd512bf4f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -500,7 +500,7 @@ class StockEntry(StockController): if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty: + elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) d.basic_amount = d.basic_rate * d.qty diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index c16a41c24fa..7b9c129804e 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -23,6 +23,7 @@ "image", "image_view", "quantity_and_rate", + "set_basic_rate_manually", "qty", "basic_rate", "basic_amount", @@ -491,12 +492,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", + "fieldname": "set_basic_rate_manually", + "fieldtype": "Check", + "label": "Set Basic Rate Manually", + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-23 19:19:28.539769", + "modified": "2020-06-08 12:57:03.172887", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From d5b97d69965efbfbabde144728852f20d9c6d58c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2020 19:51:55 +0530 Subject: [PATCH 371/410] Update CODEOWNERS --- CODEOWNERS | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5e1113d34f9..7cf65a7a732 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,17 +3,16 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -* @nabinhait -manufacturing/ @rohitwaghchaure +manufacturing/ @rohitwaghchaure @marination accounts/ @deepeshgarg007 @nextchamp-saqib -loan_management/ @deepeshgarg007 -pos* @nextchamp-saqib -assets/ @nextchamp-saqib +loan_management/ @deepeshgarg007 @rohitwaghchaure +pos* @nextchamp-saqib @rohitwaghchaure +assets/ @nextchamp-saqib @deepeshgarg007 stock/ @marination @rohitwaghchaure -buying/ @marination @rohitwaghchaure -hr/ @Anurag810 -projects/ @hrwX -support/ @hrwX -healthcare/ @ruchamahabal -erpnext_integrations/ @Mangesh-Khairnar +buying/ @marination @deepeshgarg007 +hr/ @Anurag810 @rohitwaghchaure +projects/ @hrwX @nextchamp-saqib +support/ @hrwX @marination +healthcare/ @ruchamahabal @marination +erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib requirements.txt @gavindsouza From d27d88c3e45360ebb5f7ed18f4c2db8910bb93f5 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Mon, 8 Jun 2020 22:52:44 +0200 Subject: [PATCH 372/410] feat: bill all hours by default --- erpnext/projects/doctype/timesheet/timesheet.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 3eea390ff31..bd48e550070 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -206,6 +206,9 @@ frappe.ui.form.on("Timesheet Detail", { update_billing_hours(frm, cdt, cdn); update_time_rates(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); + + // bill all `hours` by default + frappe.model.set_value(cdt, cdn, "billing_hours", locals[cdt][cdn].hours); }, activity_type: function(frm, cdt, cdn) { From e5fe00cf58bf1aa0edbb07bfbc70dc66c60023f2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 9 Jun 2020 17:41:27 +0530 Subject: [PATCH 373/410] Revert "fix: docfield of sales_order is not fetching route options for new doc (#21123)" This reverts commit 5c54adec28c7fda2e290a1bd2bfd1ab7f3c751d0. --- erpnext/projects/doctype/project/project.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 58629634968..3570a0f2be4 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Project", { }; }, onload: function (frm) { - var so = frm.get_docfield("Project", "sales_order"); + var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { if (frm.is_new()) return; return { @@ -135,4 +135,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); -} +} \ No newline at end of file From 088ab75083efe44fa9b4a1e6b2a67a79c8ed4c83 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 9 Jun 2020 16:09:40 +0200 Subject: [PATCH 374/410] fix: move feature into update_billing_hours --- erpnext/projects/doctype/timesheet/timesheet.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index bd48e550070..defc18bf4e9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -206,9 +206,6 @@ frappe.ui.form.on("Timesheet Detail", { update_billing_hours(frm, cdt, cdn); update_time_rates(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); - - // bill all `hours` by default - frappe.model.set_value(cdt, cdn, "billing_hours", locals[cdt][cdn].hours); }, activity_type: function(frm, cdt, cdn) { @@ -261,7 +258,12 @@ var calculate_end_time = function(frm, cdt, cdn) { var update_billing_hours = function(frm, cdt, cdn){ var child = locals[cdt][cdn]; - if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + if(!child.billable) { + frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + } else { + // bill all hours by default + frappe.model.set_value(cdt, cdn, "billing_hours", child.hours); + } }; var update_time_rates = function(frm, cdt, cdn){ From 934e30ecac59dcd6aa5dcb06700c37a51209689c Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 10 Jun 2020 11:07:43 +0530 Subject: [PATCH 375/410] update program course (#22166) --- .../program_course/program_course.json | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json index a24e88a8611..940358e4e9b 100644 --- a/erpnext/education/doctype/program_course/program_course.json +++ b/erpnext/education/doctype/program_course/program_course.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2015-09-07 14:37:01.886859", "doctype": "DocType", "editable_grid": 1, @@ -16,26 +17,33 @@ "in_list_view": 1, "label": "Course", "options": "Course", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, - { + { + "fetch_from": "course.course_name", "fieldname": "course_name", "fieldtype": "Data", "in_list_view": 1, "label": "Course Name", - "fetch_from": "course.course_name", - "read_only":1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "required", "fieldtype": "Check", "in_list_view": 1, - "label": "Mandatory" + "label": "Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "istable": 1, - "modified": "2019-06-12 12:42:12.845972", + "links": [], + "modified": "2020-06-09 18:56:10.213241", "modified_by": "Administrator", "module": "Education", "name": "Program Course", @@ -45,4 +53,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From 402c347f83827090a4ce23ae73cdf50437333de8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 10 Jun 2020 12:22:12 +0530 Subject: [PATCH 376/410] fix: set cost center in child table --- erpnext/hr/doctype/expense_claim/expense_claim.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index fb2310396b7..6bb9af98263 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -243,7 +243,6 @@ frappe.ui.form.on("Expense Claim", { }, update_employee_advance_claimed_amount: function(frm) { - console.log("update_employee_advance_claimed_amount") let amount_to_be_allocated = frm.doc.grand_total; $.each(frm.doc.advances || [], function(i, advance){ if (amount_to_be_allocated >= advance.unclaimed_amount){ @@ -295,6 +294,16 @@ frappe.ui.form.on("Expense Claim", { frm.events.get_advances(frm); }, + cost_center: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ + (frm.doc.expenses || []).forEach(function(d) { + if (!d.cost_center){ + d.cost_center = frm.doc.cost_center; + } + }); + }, get_taxes: function(frm) { if(frm.doc.taxes) { frappe.call({ @@ -338,8 +347,7 @@ frappe.ui.form.on("Expense Claim", { frappe.ui.form.on("Expense Claim Detail", { expenses_add: function(frm, cdt, cdn) { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]); + frm.events.set_child_cost_center(frm); }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; From 452b7760df4d96712aba27f03c2dfd9a828f2b04 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 10 Jun 2020 12:48:21 +0530 Subject: [PATCH 377/410] fix: dependency for leave Period in Desk page --- erpnext/hr/desk_page/hr/hr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 7ac000b011a..1c24444fdd2 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Leaves", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -93,7 +93,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-28 13:36:07.710600", + "modified": "2020-06-10 12:41:41.695669", "modified_by": "Administrator", "module": "HR", "name": "HR", From afe8e88cb8fa6e75a4016d3a76988882d81378b4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jun 2020 17:55:24 +0530 Subject: [PATCH 378/410] fix: Cannot read property 'has_batch_no' of undefined --- erpnext/public/js/controllers/transaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 524a95804fd..2ffc728df0d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -552,7 +552,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message.has_batch_no || r.message.has_serial_no) { + if (r.message && + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); From fdddb679eda5015e8ab0bbefb5853a14d2608b6e Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 10 Jun 2020 18:22:59 +0530 Subject: [PATCH 379/410] fix: Prioritize Default Customer Price List in Portal --- erpnext/shopping_cart/cart.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index d04c8c25a34..7096c17fb18 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -337,21 +337,17 @@ def set_price_list_and_rate(quotation, cart_settings): def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list - - # check if customer price list exists + party_name = quotation.get("party_name") if quotation else get_party().get("name") selling_price_list = None - if quotation and quotation.get("party_name"): - selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') - # else check for territory based price list + # check if default customer price list exists + if party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + # check default price list in shopping cart if not selling_price_list: selling_price_list = cart_settings.price_list - party_name = quotation.get("party_name") if quotation else get_party().get("name") - - if not selling_price_list and party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) - if quotation: quotation.selling_price_list = selling_price_list From 08fee1226606fa19a0d86eb28cbb135bf0b18e96 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 18:33:24 +0530 Subject: [PATCH 380/410] fix: Item-wise sales and purchase register export --- .../item_wise_purchase_register.py | 7 ------- .../item_wise_sales_register.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 15 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 9777ed1dfde..3445df7206f 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 @@ -265,13 +265,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] 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 bb78ee2d675..a05dcd75ce5 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 @@ -223,7 +223,7 @@ def get_columns(additional_table_columns, filters): } ] - if filters.get('group_by') != 'Terriotory': + if filters.get('group_by') != 'Territory': columns.extend([ { 'label': _("Territory"), @@ -304,13 +304,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] @@ -536,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency, 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] From a6acf18f6c66ae2c55f35a76d9e8dfd35df8600d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 18:33:24 +0530 Subject: [PATCH 381/410] fix: Item-wise sales and purchase register export --- .../item_wise_purchase_register.py | 7 ------- .../item_wise_sales_register.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 15 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 127f3133f5b..6c5dec957b7 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 @@ -266,13 +266,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] 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 0c8957ae441..0c71deb7500 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 @@ -224,7 +224,7 @@ def get_columns(additional_table_columns, filters): } ] - if filters.get('group_by') != 'Terriotory': + if filters.get('group_by') != 'Territory': columns.extend([ { 'label': _("Territory"), @@ -305,13 +305,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] @@ -537,6 +530,13 @@ def get_tax_accounts(item_list, columns, company_currency, 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] From d60d2e18263f1645a6122818b37146190b4706f2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 10 Jun 2020 19:10:56 +0530 Subject: [PATCH 382/410] fix: only auto-set serial nos and batches if allowed in Stock Settings (develop) (#21781) * fix: only auto-set serial nos and batches if allowed in Stock Settings * fix: bug with setting disabled batch no in Pick List * fix: remove auto-set batch variable Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 93b29c8daff..4b8b594ed9d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -119,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos, + 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, 'batch_no': item_location.batch_no })) @@ -206,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s + and batch.disabled = 0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -471,4 +474,4 @@ def update_common_item_properties(item, location): item.material_request = location.material_request item.serial_no = location.serial_no item.batch_no = location.batch_no - item.material_request_item = location.material_request_item \ No newline at end of file + item.material_request_item = location.material_request_item From 7963e2b708db5c0240b228846f17d999373f59b3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 19:57:49 +0530 Subject: [PATCH 383/410] fix: Party validation for inter-warehouse transaction --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 57dc17936da..8b5d4d110cc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1450,11 +1450,17 @@ def get_inter_company_details(doc, doctype): parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Customer", doc.customer, "represents_company") + if not parties: + frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Supplier", doc) else: parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company") + if not parties: + frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Customer", doc) return { From a0fd97f2ac1598ce7e3457aa4b30b19720906537 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 22:15:27 +0530 Subject: [PATCH 384/410] fix: Update payment schedule based on payment terms --- .../doctype/payment_entry/payment_entry.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d2245d6a6d6..15e51bbd995 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -319,7 +319,7 @@ class PaymentEntry(AccountsController): invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += reference.allocated_amount - if not invoice_paid_amount_map.get(reference.reference_name): + if not invoice_paid_amount_map.get(key): payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, fields=['paid_amount', 'payment_amount', 'payment_term']) for term in payment_schedule: @@ -332,12 +332,14 @@ class PaymentEntry(AccountsController): frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) else: - outstanding = invoice_paid_amount_map.get(key)['outstanding'] + outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding')) + if amount > outstanding: frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) - frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s - WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) + if amount and outstanding: + frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s + WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) def set_status(self): if self.docstatus == 2: @@ -1091,17 +1093,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: - references.append({ - 'reference_doctype': dt, - 'reference_name': dn, - 'bill_no': doc.get('bill_no'), - 'due_date': doc.get('due_date'), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, + payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount, payment_term.precision('payment_amount')) - }) + + if payment_term_outstanding: + references.append({ + 'reference_doctype': dt, + 'reference_name': dn, + 'bill_no': doc.get('bill_no'), + 'due_date': doc.get('due_date'), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'payment_term': payment_term.payment_term, + 'allocated_amount': payment_term_outstanding + }) return references From 8d61a0abefc5604ae0424dd03cfc1c62f1b8abf3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 13:34:25 +0530 Subject: [PATCH 385/410] fix: use string to verify webhook --- erpnext/non_profit/doctype/membership/membership.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 4b932425b28..c4f43185f78 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -77,7 +77,7 @@ def verify_signature(data): @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): - data = frappe.request.get_data() + data = frappe.request.get_data(as_text=True) verify_signature(data) if isinstance(data, six.string_types): From 84fcc55a36fef6caa1ca45056bdf7d0d8711a0de Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 13:34:40 +0530 Subject: [PATCH 386/410] refactor: do nothing if member is not found --- erpnext/non_profit/doctype/membership/membership.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index c4f43185f78..7a0caed621e 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -62,7 +62,10 @@ def get_member_based_on_subscription(subscription_id, email): 'subscription_id': subscription_id, 'email_id': email }, order_by="creation desc") - return frappe.get_doc("Member", members[0]['name']) + try: + return frappe.get_doc("Member", members[0]['name']) + except: + return None def verify_signature(data): signature = frappe.request.headers.get('X-Razorpay-Signature') @@ -96,7 +99,10 @@ def trigger_razorpay_subscription(*args, **kwargs): except Exception as e: error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed")) notify_failure(error_log) - raise e + return False + + if not member: + return False if data.event == "subscription.activated": member.customer_id = payment.customer_id From 49d51b8ce72f7bcdb8931a3c23fde76e43c7a7b9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 14:24:07 +0530 Subject: [PATCH 387/410] feat: check duplicate contact explicitly --- erpnext/non_profit/doctype/member/member.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 571f87af874..aaa56ba118d 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -92,6 +92,10 @@ def create_customer(user_details): }) contact.insert() + + except frappe.DuplicateEntryError: + return customer.name + except Exception as e: frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed")) pass From 8c24810f309f525d5ae334862af87b15cccb3073 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 14:37:50 +0530 Subject: [PATCH 388/410] refactor: ignore mandatory for customer --- erpnext/non_profit/doctype/member/member.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index aaa56ba118d..d1294ccc08b 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -77,6 +77,7 @@ def create_customer(user_details): customer = frappe.new_doc("Customer") customer.customer_name = user_details.fullname customer.customer_type = "Individual" + customer.flags.ignore_mandatory = True customer.insert(ignore_permissions=True) try: @@ -91,7 +92,7 @@ def create_customer(user_details): "link_name": customer.name }) - contact.insert() + contact.save() except frappe.DuplicateEntryError: return customer.name From 596560c3c219d218e23eef575e18e7d0627bcf6f Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 16:39:03 +0530 Subject: [PATCH 389/410] fix: Don't prompt for Quality Inspection on Return Documents. --- erpnext/controllers/stock_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 90d293088b0..2ce41590cff 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.is_return: + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 44b8cf4fefb3d9228a6b09220cf7e4b7532ad2df Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Thu, 11 Jun 2020 19:31:41 +0530 Subject: [PATCH 390/410] enable 'user cannot create' for payment request (#22044) Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/payment_request/payment_request.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 7508683c080..eef6be1a7a1 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -349,9 +349,10 @@ "read_only": 1 } ], + "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-05-08 10:23:02.815237", + "modified": "2020-05-29 17:38:49.392713", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", From c9a6e4f151faf4823e1d508c34eada28ab4544ff Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 19:54:44 +0530 Subject: [PATCH 391/410] fix: Check for Company before rendering tree in Account Tree --- erpnext/accounts/doctype/account/account_tree.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index f62d07668de..28b090bdadb 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = { on_change: function() { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); + if (!company) { + frappe.throw(__("Please set a Company")); + } frappe.call({ method: "erpnext.accounts.doctype.account.account.get_root_company", args: { From 6b55f66f02b8ee2bfc9f1a11bc176c776f03366e Mon Sep 17 00:00:00 2001 From: John Clarke Date: Thu, 11 Jun 2020 10:24:48 -0600 Subject: [PATCH 392/410] =?UTF-8?q?Stock=20Ageing=20TypeError:=20=E2=80=98?= =?UTF-8?q?<=E2=80=99=20not=20supported=20between=20instances=20of=20?= =?UTF-8?q?=E2=80=98int=E2=80=99=20and=20=E2=80=98str=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposed fix re this error report https://discuss.erpnext.com/t/stock-ageing-report-typeerror-not-supported-between-instances-of-int-and-str/62605 --- erpnext/stock/report/stock_ageing/stock_ageing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index af997801551..53bdfb523d2 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -180,14 +180,14 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < batch[0] <= qty_to_pop: + if 0 < cint(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= batch[0] + qty_to_pop -= cint(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - batch[0] -= qty_to_pop + cint(batch[0]) -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 @@ -262,4 +262,4 @@ def get_chart_data(data, filters): ] }, "type" : "bar" - } \ No newline at end of file + } From faf33072c8aa1b4ebbe83c42253198131b1d8cc9 Mon Sep 17 00:00:00 2001 From: Alirio Castro Date: Thu, 11 Jun 2020 23:32:53 -0400 Subject: [PATCH 393/410] fix: key was mispelled --- .../itemwise_recommended_reorder_level.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index 9a972104a27..5df3fa8067b 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -8,7 +8,7 @@ from frappe.utils import getdate, flt def execute(filters=None): if not filters: filters = {} - float_preceision = frappe.db.get_default("float_preceision") + float_precision = frappe.db.get_default("float_precision") condition = get_condition(filters) @@ -25,7 +25,7 @@ def execute(filters=None): data = [] for item in items: total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0)) - avg_daily_outgoing = flt(total_outgoing / diff, float_preceision) + avg_daily_outgoing = flt(total_outgoing / diff, float_precision) reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock) data.append([item.name, item.item_name, item.item_group, item.brand, item.description, From fd3d976e067464cb5f4ec347fb79538b68c13cac Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 12 Jun 2020 12:30:59 +0530 Subject: [PATCH 394/410] Update erpnext/controllers/stock_controller.py Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- 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 2ce41590cff..2888c764ef7 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - if not self.is_return: + if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 90957b7f747b89e85037aeb713043a4e4437e96d Mon Sep 17 00:00:00 2001 From: Vignesh S Date: Fri, 12 Jun 2020 12:32:55 +0530 Subject: [PATCH 395/410] feat(Attendance): Add In and Out time to attendance (#21547) * feat(Attendance): Add In and Out time to attendance Co-authored-by: Karthikeyan S * fix:add depends in attendance IN time and OUT time Co-authored-by: Karthikeyan S --- erpnext/hr/doctype/attendance/attendance.json | 35 ++++++++++++++++--- .../employee_checkin/employee_checkin.py | 6 ++-- erpnext/hr/doctype/shift_type/shift_type.py | 13 +++---- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index 906f6f77f21..a656a7ea5f7 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -19,11 +19,15 @@ "attendance_date", "company", "department", - "shift", "attendance_request", - "amended_from", + "details_section", + "shift", + "in_time", + "out_time", + "column_break_18", "late_entry", - "early_exit" + "early_exit", + "amended_from" ], "fields": [ { @@ -172,13 +176,36 @@ "fieldname": "early_exit", "fieldtype": "Check", "label": "Early Exit" + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "depends_on": "shift", + "fieldname": "in_time", + "fieldtype": "Datetime", + "label": "In Time", + "read_only": 1 + }, + { + "depends_on": "shift", + "fieldname": "out_time", + "fieldtype": "Datetime", + "label": "Out Time", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-04-11 11:40:14.319496", + "modified": "2020-05-29 13:51:37.177231", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 86705121ac5..15fbd4e0153 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N return doc -def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None): +def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None): """Creates an attendance and links the attendance to the Employee Checkin. Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown. @@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki 'company': employee_doc.company, 'shift': shift, 'late_entry': late_entry, - 'early_exit': early_exit + 'early_exit': early_exit, + 'in_time': in_time, + 'out_time': out_time } attendance = frappe.get_doc(doc_dict).insert() attendance.submit() diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index d56080eecd4..19735648aa9 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -28,13 +28,14 @@ class ShiftType(Document): logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])): single_shift_logs = list(group) - attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs) - mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name) + attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs) + mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name) for employee in self.get_assigned_employee(self.process_attendance_after, True): self.mark_absent_for_dates_with_no_attendance(employee) def get_attendance(self, logs): - """Return attendance_status, working_hours for a set of logs belonging to a single shift. + """Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time + for a set of logs belonging to a single shift. Assumtion: 1. These logs belongs to an single shift, single employee and is not in a holiday date. 2. Logs are in chronological order @@ -48,10 +49,10 @@ class ShiftType(Document): early_exit = True if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent: - return 'Absent', total_working_hours, late_entry, early_exit + return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day: - return 'Half Day', total_working_hours, late_entry, early_exit - return 'Present', total_working_hours, late_entry, early_exit + return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time + return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time def mark_absent_for_dates_with_no_attendance(self, employee): """Marks Absents for the given employee on working days in this shift which have no attendance marked. From ccd03bc1206ebfd72d1e7c14d53c764e92d0c425 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 25 Feb 2020 17:56:15 +0530 Subject: [PATCH 396/410] feat: notify credit controller role users with credit limit extension requests --- erpnext/selling/doctype/customer/customer.py | 50 +++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a6889e080d9..ac3bc201e96 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -3,16 +3,19 @@ from __future__ import unicode_literals import frappe +import json from frappe.model.naming import set_name_by_naming_series -from frappe import _, msgprint, throw +from frappe import _, msgprint import frappe.defaults -from frappe.utils import flt, cint, cstr, today +from frappe.utils import flt, cint, cstr, today, get_formatted_email from frappe.desk.reportview import build_match_conditions, get_filters_cond from erpnext.utilities.transaction_base import TransactionBase from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.rename_doc import update_linked_doctypes from frappe.model.mapper import get_mapped_doc +from frappe.utils.user import get_users_with_role + class Customer(TransactionBase): def get_feed(self): @@ -378,10 +381,45 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, .format(customer, customer_outstanding, credit_limit)) # If not authorized person raise exception - credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller') - if not credit_controller or credit_controller not in frappe.get_roles(): - throw(_("Please contact to the user who have Sales Master Manager {0} role") - .format(" / " + credit_controller if credit_controller else "")) + credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller') + if not credit_controller_role or credit_controller_role not in frappe.get_roles(): + # form a list of emails for the credit controller users + credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager") + + # form a list of emails and names to show to the user + credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})] + credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list] + + if not credit_controller_users: + frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) + + message = """Please contact any of the following users to extend the credit limits for {0}: +

    • {1}
    """.format(customer, '
  • '.join(credit_controller_users)) + + # if the current user does not have permissions to override credit limit, + # prompt them to send out an email to the controller users + frappe.msgprint(message, + title="Notify", + raise_exception=1, + primary_action={ + 'label': 'Send Email', + 'server_action': 'erpnext.selling.doctype.customer.customer.send_emails', + 'args': { + 'customer': customer, + 'customer_outstanding': customer_outstanding, + 'credit_limit': credit_limit, + 'credit_controller_users_list': credit_controller_users_list + } + } + ) + +@frappe.whitelist() +def send_emails(args): + args = json.loads(args) + subject = (_("Credit limit reached for customer {0}").format(args.get('customer'))) + message = (_("Credit limit has been crossed for customer {0} ({1}/{2})") + .format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit'))) + frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message) def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): # Outstanding based on GL Entries From 86ea75e49e0e952d35aadc9579a533bb37e96d3e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 12 Jun 2020 13:13:14 +0530 Subject: [PATCH 397/410] fix(HR): Change Due Advance Amount to Pending Amount (#22123) * rename 'Due Advance Amount' field to 'Pending Amount' * changed fieldname and references for easier debugging * added patch for moving data * added newline added Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../employee_advance/employee_advance.js | 4 ++-- .../employee_advance/employee_advance.json | 20 +++++++++---------- .../employee_advance/employee_advance.py | 2 +- erpnext/patches.txt | 3 ++- ...ve_due_advance_amount_to_pending_amount.py | 9 +++++++++ 5 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 6cc49cfff23..cba8ee9a404 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount", + method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, "posting_date": frm.doc.posting_date }, callback: function(r) { - frm.set_value("due_advance_amount",r.message); + frm.set_value("pending_amount",r.message); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 8c5ce42d870..0d909138719 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -19,7 +19,7 @@ "column_break_11", "advance_amount", "paid_amount", - "due_advance_amount", + "pending_amount", "claimed_amount", "return_amount", "section_break_7", @@ -102,14 +102,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "label": "Due Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "claimed_amount", "fieldtype": "Currency", @@ -177,11 +169,19 @@ "fieldname": "repay_unclaimed_amount_from_salary", "fieldtype": "Check", "label": "Repay unclaimed amount from salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "Company:company:default_currency", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-03-06 15:11:33.747535", + "modified": "2020-06-12 12:42:39.833818", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index db39eff0e4d..a49dfcfc2a2 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -95,7 +95,7 @@ class EmployeeAdvance(Document): frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() -def get_due_advance_amount(employee, posting_date): +def get_pending_amount(employee, posting_date): employee_due_amount = frappe.get_all("Employee Advance", \ filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \ fields = ["advance_amount", "paid_amount"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0421f43c64..1f5d4d563ae 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price @@ -694,4 +695,4 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports -erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 00000000000..f1ffaf9d2d4 --- /dev/null +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move from due_advance_amount to pending_amount ''' + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 182c1f860b2c0c53f509559daa7c334fabfbcd0d Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Fri, 12 Jun 2020 15:16:20 +0530 Subject: [PATCH 398/410] fix(patch): escape special characters in company field --- .../patches/v13_0/set_company_field_in_healthcare_doctypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index a7d4c665a1e..be5e30f3074 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -7,4 +7,4 @@ def execute(): for entry in doctypes: if frappe.db.exists('DocType', entry): frappe.reload_doc('Healthcare', 'doctype', entry) - frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) + frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company))) From 677978dd00e92533d7897dec041a901c0e3cbba9 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Fri, 12 Jun 2020 05:53:24 -0600 Subject: [PATCH 399/410] Update stock_ageing.py As reviewer Deepesh Garg advised thanks! batch[0] can be int of float - see line 49 - so change proposed cint to flt --- erpnext/stock/report/stock_ageing/stock_ageing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 53bdfb523d2..eea2312a245 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -180,14 +180,14 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < cint(batch[0]) <= qty_to_pop: + if 0 < flt(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= cint(batch[0]) + qty_to_pop -= flt(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - cint(batch[0]) -= qty_to_pop + flt(batch[0]) -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From 7558e64d6dd2b1e34f8ff9c976e479f39c0f69ee Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 12 Jun 2020 17:37:59 +0530 Subject: [PATCH 400/410] fix: set_query in leave application (#22197) --- .../hr/doctype/leave_application/leave_application.js | 11 +++++++++-- .../hr/doctype/leave_application/leave_application.py | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 15ce468c138..fb1f2c00b13 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", { make_dashboard: function(frm) { var leave_details; + let lwps; if (frm.doc.employee) { frappe.call({ method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details", @@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", { if (!r.exc && r.message['leave_approver']) { frm.set_value('leave_approver', r.message['leave_approver']); } + lwps = r.message["lwps"]; } }); $("div").remove(".form-dashboard-section.custom"); @@ -70,12 +72,17 @@ frappe.ui.form.on("Leave Application", { }) ); frm.dashboard.show(); + let allowed_leave_types = Object.keys(leave_details); + + // lwps should be allowed, lwps don't have any allocation + allowed_leave_types = allowed_leave_types.concat(lwps); + frm.set_query('leave_type', function(){ return { filters : [ - ['leave_type_name', 'in', Object.keys(leave_details)] + ['leave_type_name', 'in', allowed_leave_types] ] - } + }; }); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index c2b4cdf1058..0423824c0e9 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass from frappe.model.document import Document class LeaveApplication(Document): - def get_feed(self): return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) @@ -463,9 +462,14 @@ def get_leave_details(employee, date): "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} + #is used in set query + lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1}) + lwps = [lwp.name for lwp in lwps] + ret = { 'leave_allocation': leave_allocation, - 'leave_approver': get_leave_approver(employee) + 'leave_approver': get_leave_approver(employee), + 'lwps': lwps } return ret From 85f237257a57cc6e92d04c57515e69bab52661e8 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Fri, 12 Jun 2020 07:22:14 -0600 Subject: [PATCH 401/410] Thanks so much for your pointer Marica - that violation is clear and obvious to me in retrospect, but a good relearning experience in any case --- erpnext/stock/report/stock_ageing/stock_ageing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index eea2312a245..723ed5c1c46 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -187,7 +187,7 @@ def get_fifo_queue(filters, sle=None): transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - flt(batch[0]) -= qty_to_pop + batch[0] -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From 5b8a47d0729243cceb52fba807d33b730e431715 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sat, 13 Jun 2020 01:04:32 +0530 Subject: [PATCH 402/410] fix: patch for expense claim (#22226) --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1f5d4d563ae..a0707b77ca7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +execute:frappe.reload_doc("HR", "doctype", "Employee Advance") erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) From 8bc414620b7a819aab05a92ca25bf8c3e5ddac01 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 13 Jun 2020 13:11:43 +0530 Subject: [PATCH 403/410] opportunity-dashboard-fix --- erpnext/crm/doctype/opportunity/opportunity_dashboard.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd23..68f0104fd6c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] From 15231aa60d948828acaab798732049383414b007 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 19:16:37 +0530 Subject: [PATCH 404/410] fix: Data not appearing properly for some fiscal_year in financial statemets --- erpnext/accounts/report/financial_statements.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 0339e4920a8..393c5d3aa60 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -56,9 +56,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ to_date = add_months(start_date, months_to_add) start_date = to_date - if to_date == get_first_day(to_date): - # if to_date is the first day, get the last day of previous month - to_date = add_days(to_date, -1) + # Subtract one day from to_date, as it may be first day in next fiscal year or month + to_date = add_days(to_date, -1) if to_date <= year_end_date: # the normal case From d80d442aa1f82a32a9286320270243cba73675f7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 21:12:19 +0530 Subject: [PATCH 405/410] fix: Do not select cancelled entries in financial statements --- erpnext/accounts/report/financial_statements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 0339e4920a8..bb33a0025a3 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -418,6 +418,7 @@ def set_gl_entries_by_account( where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s + and is_cancelled = 0 {distributed_cost_center_query} order by account, posting_date""".format( additional_conditions=additional_conditions, From cc4943d54d81270ef90fe35687a3f45066d5c7cc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 21:13:52 +0530 Subject: [PATCH 406/410] fix: Distributed cost center query updation --- erpnext/accounts/report/financial_statements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index bb33a0025a3..769eb0a57f0 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -406,6 +406,7 @@ def set_gl_entries_by_account( FROM `tabDistributed Cost Center` WHERE cost_center IN %(cost_center)s AND parent NOT IN %(cost_center)s + AND is_cancelled = 0 GROUP BY parent ) as DCC_allocation WHERE company=%(company)s From 67600776747c0a0079e3fa33e7f66cc75790a8d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 22:40:23 +0530 Subject: [PATCH 407/410] fix: Billing address in for Purchase documents --- .../purchase_invoice/purchase_invoice.json | 616 +++++++++++++----- .../purchase_order/purchase_order.json | 508 +++++++++++---- erpnext/public/js/controllers/buying.js | 7 + .../purchase_receipt/purchase_receipt.json | 456 ++++++++++--- 4 files changed, 1215 insertions(+), 372 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 98ba5c72ae2..829c34da67c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -42,6 +42,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -168,7 +170,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -180,7 +184,9 @@ "options": "ACC-PINV-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -192,7 +198,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -204,7 +212,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -212,21 +222,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -234,19 +250,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -256,13 +278,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -274,7 +300,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -283,6 +311,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -291,7 +321,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -303,44 +335,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -348,11 +394,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -360,13 +410,17 @@ "label": "Supplier Invoice Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -376,26 +430,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -403,51 +465,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -456,7 +534,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -465,18 +545,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -484,14 +570,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -500,11 +590,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -512,7 +606,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -522,11 +618,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -534,7 +634,9 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -545,25 +647,33 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -573,42 +683,56 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", "options": "Purchase Receipt Item Supplied", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -616,7 +740,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -626,18 +752,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -647,42 +779,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -691,7 +837,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -699,13 +847,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -714,13 +866,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -730,7 +886,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -740,7 +898,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -750,11 +910,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -764,7 +928,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -774,7 +940,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -782,14 +950,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -797,7 +969,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -805,28 +979,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -836,7 +1020,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -845,7 +1031,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -855,7 +1043,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -864,13 +1054,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -881,7 +1075,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -890,7 +1086,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -900,7 +1098,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -909,7 +1109,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -920,7 +1122,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -931,14 +1135,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -946,30 +1154,40 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", "fieldtype": "Date", "hidden": 1, - "label": "Clearance Date" + "label": "Clearance Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -978,7 +1196,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -987,7 +1207,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -995,7 +1217,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1003,7 +1227,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1012,11 +1238,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1024,7 +1254,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1032,7 +1264,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1042,13 +1276,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1056,7 +1294,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1066,20 +1306,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1087,7 +1333,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1095,25 +1343,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1121,7 +1377,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1129,11 +1387,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1145,14 +1407,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1161,7 +1427,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1172,7 +1440,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1182,7 +1452,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1192,7 +1464,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1202,11 +1476,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1215,14 +1493,18 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", "label": "Inter Company Invoice Reference", "options": "Sales Invoice", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1231,14 +1513,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1247,7 +1533,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1256,11 +1544,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1269,24 +1561,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1294,7 +1594,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1302,14 +1604,32 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-06-13 22:26:30.800199", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a4f60fbba5c..7145fea5c5c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -38,6 +38,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -135,7 +137,9 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -145,7 +149,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -157,7 +163,9 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -170,14 +178,18 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests" + "label": "Get Items from Open Material Requests", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -186,7 +198,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -198,13 +212,17 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -216,27 +234,35 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By" + "label": "Required By", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No" + "label": "Order Confirmation No", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date" + "label": "Order Confirmation Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -248,19 +274,25 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship" + "label": "Drop Ship", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -268,31 +300,41 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_email", @@ -300,46 +342,60 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -347,32 +403,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -382,7 +448,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -392,18 +460,24 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -411,14 +485,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -427,11 +505,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -439,11 +521,15 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -452,25 +538,33 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -480,26 +574,34 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -509,17 +611,23 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -527,7 +635,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -538,18 +648,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -559,20 +675,26 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -581,22 +703,30 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_52", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -604,13 +734,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -619,13 +753,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -635,7 +773,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -645,7 +785,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -656,11 +798,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -670,7 +816,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -680,7 +828,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -688,14 +838,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -703,7 +857,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -711,28 +867,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -743,7 +909,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -752,7 +920,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -762,7 +932,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -772,12 +944,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -787,7 +963,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -796,20 +974,26 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -818,7 +1002,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advance_paid", @@ -827,19 +1013,25 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -847,7 +1039,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -856,7 +1050,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -865,21 +1061,27 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -894,7 +1096,9 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "ref_sq", @@ -905,7 +1109,9 @@ "oldfieldname": "ref_sq", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -915,18 +1121,24 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -936,7 +1148,9 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -946,7 +1160,9 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -956,6 +1172,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -966,7 +1184,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -978,11 +1198,15 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -990,19 +1214,25 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1010,7 +1240,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1018,11 +1250,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1031,44 +1267,72 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category" + "options": "Tax Category", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking" + "label": "Tracking", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_75", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:13:14.186280", + "modified": "2020-06-13 22:25:47.333850", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 9c561894764..a4cc68b3e2c 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); + me.frm.set_query('billing_address', erpnext.queries.company_address_query); + if(this.frm.fields_dict.supplier) { this.frm.set_query("supplier", function() { return{ query: "erpnext.controllers.queries.supplier_query" }}); @@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ "shipping_address_display", true); }, + billing_address: function() { + erpnext.utils.get_address_display(this.frm, "billing_address", + "billing_address_display", true); + }, + tc_name: function() { this.get_terms(); }, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 467a206d188..44d5f690285 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -32,6 +32,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -130,13 +132,17 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -147,7 +153,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -159,7 +167,9 @@ "options": "MAT-PRE-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -174,6 +184,8 @@ "print_width": "150px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -184,18 +196,24 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_delivery_note", "fieldtype": "Data", - "label": "Supplier Delivery Note" + "label": "Supplier Delivery Note", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -210,6 +228,8 @@ "print_width": "100px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -223,6 +243,8 @@ "print_hide": 1, "print_width": "100px", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -231,7 +253,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -245,6 +269,8 @@ "print_width": "150px", "remember_last_selected_value": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -254,7 +280,9 @@ "label": "Is Return", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_return", @@ -264,46 +292,60 @@ "no_copy": 1, "options": "Purchase Receipt", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -311,32 +353,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -346,7 +398,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which supplier's currency is converted to company's base currency", @@ -357,13 +411,17 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -371,7 +429,9 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -380,7 +440,9 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -388,7 +450,9 @@ "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -397,11 +461,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Accepted Warehouse' in each row of the items table.", @@ -409,7 +477,9 @@ "fieldtype": "Link", "label": "Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Rejected Warehouse' in each row of the items table.", @@ -420,11 +490,15 @@ "oldfieldname": "rejected_warehouse", "oldfieldtype": "Link", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -434,7 +508,9 @@ "oldfieldname": "is_subcontracted", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -447,13 +523,17 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -463,20 +543,26 @@ "oldfieldname": "purchase_receipt_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", @@ -485,7 +571,9 @@ "label": "Get Current Stock", "oldfieldtype": "Button", "options": "get_current_stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -496,7 +584,9 @@ "oldfieldtype": "Section Break", "options": "fa fa-table", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -507,18 +597,24 @@ "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break0", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -526,7 +622,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -539,18 +637,24 @@ "print_width": "150px", "read_only": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { "fieldname": "column_break_27", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -560,42 +664,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_col", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", - "options": "Shipping Rule" + "options": "Shipping Rule", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -604,7 +722,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -612,13 +732,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -627,13 +751,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -643,7 +771,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -653,7 +783,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -663,12 +795,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break3", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -679,7 +815,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -689,7 +827,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -697,14 +837,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -712,7 +856,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -720,28 +866,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_44", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -751,7 +907,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -760,7 +918,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -769,7 +929,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -779,11 +941,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -793,7 +959,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -802,7 +970,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -812,7 +982,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -821,13 +993,17 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -836,7 +1012,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -845,14 +1023,18 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -861,7 +1043,9 @@ "label": "Bill No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -870,7 +1054,9 @@ "label": "Bill Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -878,7 +1064,9 @@ "fieldtype": "Section Break", "label": "More Information", "oldfieldtype": "Section Break", - "options": "fa fa-file-text" + "options": "fa fa-file-text", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -895,6 +1083,8 @@ "read_only": 1, "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -910,6 +1100,8 @@ "print_hide": 1, "print_width": "150px", "read_only": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -919,7 +1111,9 @@ "label": "Range", "oldfieldname": "range", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", @@ -927,6 +1121,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -935,12 +1131,16 @@ "label": "% Amount Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -949,13 +1149,17 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -963,7 +1167,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -975,13 +1181,17 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -989,11 +1199,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_details", @@ -1004,6 +1218,8 @@ "options": "
    Other Details
    ", "print_hide": 1, "print_width": "30%", + "show_days": 1, + "show_seconds": 1, "width": "30%" }, { @@ -1011,13 +1227,17 @@ "fieldtype": "Small Text", "label": "Instructions", "oldfieldname": "instructions", - "oldfieldtype": "Text" + "oldfieldtype": "Text", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1025,19 +1245,25 @@ "fieldname": "transporter_info", "fieldtype": "Section Break", "label": "Transporter Details", - "options": "fa fa-truck" + "options": "fa fa-truck", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transporter_name", "fieldtype": "Data", "label": "Transporter Name", "oldfieldname": "transporter_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1048,6 +1274,8 @@ "oldfieldname": "lr_no", "oldfieldtype": "Data", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1058,6 +1286,8 @@ "oldfieldname": "lr_date", "oldfieldtype": "Date", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1066,26 +1296,48 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", "options": "Delivery Note", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-04-18 18:02:18.020763", + "modified": "2020-06-13 22:26:03.600092", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", @@ -1152,4 +1404,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file From 65e7a2e7a6696c5493d4731149078c2e16816145 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 13 Jun 2020 13:11:43 +0530 Subject: [PATCH 408/410] opportunity-dashboard-fix --- .../opportunity/opportunity_dashboard.py | 6 +- .../social_media_post/social_media_post.json | 76 ++++++++++++++----- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd23..68f0104fd6c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 2601c14b4d6..9c74aaad5fe 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -30,24 +30,32 @@ "fieldname": "text", "fieldtype": "Small Text", "label": "Tweet", - "mandatory_depends_on": "eval:doc.twitter ==1" + "mandatory_depends_on": "eval:doc.twitter ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "image", "fieldtype": "Attach Image", - "label": "Image" + "label": "Image", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "twitter", "fieldtype": "Check", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "linkedin", "fieldtype": "Check", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -56,13 +64,17 @@ "no_copy": 1, "options": "Social Media Post", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.twitter ==1", "fieldname": "content", "fieldtype": "Section Break", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -70,7 +82,9 @@ "fieldtype": "Select", "label": "Post Status", "options": "\nScheduled\nPosted\nError", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -78,7 +92,9 @@ "fieldtype": "Data", "hidden": 1, "label": "Twitter Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -86,68 +102,89 @@ "fieldtype": "Data", "hidden": 1, "label": "LinkedIn Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign_name", "fieldtype": "Link", "in_list_view": 1, "label": "Campaign", - "options": "Campaign" + "options": "Campaign", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_6", "fieldtype": "Column Break", - "label": "Share On" + "label": "Share On", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tweet_preview", - "fieldtype": "HTML" + "fieldtype": "HTML", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "depends_on": "eval:doc.linkedin==1", "fieldname": "linkedin_section", "fieldtype": "Section Break", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "attachments_section", "fieldtype": "Section Break", - "label": "Attachments" + "label": "Attachments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "linkedin_post", "fieldtype": "Text", "label": "Post", - "mandatory_depends_on": "eval:doc.linkedin ==1" + "mandatory_depends_on": "eval:doc.linkedin ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "scheduled_time", "fieldtype": "Datetime", "label": "Scheduled Time", - "read_only_depends_on": "eval:doc.post_status == \"Posted\"" + "read_only_depends_on": "eval:doc.post_status == \"Posted\"", + "show_days": 1, + "show_seconds": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 15:10:04.953713", + "modified": "2020-06-13 20:53:47.670536", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", "owner": "Administrator", "permissions": [ { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -157,6 +194,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], From 234b5f9a8c869e9fb49fbd16b099153f9972e5ff Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 14 Jun 2020 10:34:58 +0530 Subject: [PATCH 409/410] sm-post-permission-fix --- .../social_media_post/social_media_post.json | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 9c74aaad5fe..0a00dca2808 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -177,7 +177,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-06-13 20:53:47.670536", + "modified": "2020-06-14 10:31:33.961381", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", @@ -196,6 +196,34 @@ "share": 1, "submit": 1, "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "submit": 1, + "write": 1 } ], "sort_field": "modified", From d6f9a51cbb2724dd6a3675c8c85b5c40317255cb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jun 2020 14:06:12 +0530 Subject: [PATCH 410/410] fix: item none not found while making sales invoice using opening invoice creation tool --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8b5d4d110cc..5e8279bb08d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -582,14 +582,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self):