From c196d740e5f193a88ad269ef2b18ead2f350eb49 Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Fri, 2 Jun 2017 17:28:40 +0530 Subject: [PATCH 01/14] [minor] added filter for batch no field in purchase transaction (#9038) * [minor] added filter for batch no field in purchase transaction * [minor] filter the batch based on item_code instead of get_batch_no method * [minor] filter based on item_code for Purchase Invoice with update stock --- erpnext/public/js/controllers/transaction.js | 35 +++++++++++++++++++ erpnext/selling/sales_common.js | 28 --------------- .../purchase_receipt/purchase_receipt.js | 11 ------ 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6206b286da..b508fcb692 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -98,6 +98,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frm.cscript.calculate_taxes_and_totals(); }); + + var me = this; + if(this.frm.fields_dict["items"].grid.get_field('batch_no')) { + this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) { + return me.set_query_for_batch(doc, cdt, cdn) + }); + } }, onload: function() { var me = this; @@ -1129,4 +1136,32 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return method }, + + set_query_for_batch: function(doc, cdt, cdn) { + // Show item's batches in the dropdown of batch no + + var me = this; + var item = frappe.get_doc(cdt, cdn); + + if(!item.item_code) { + frappe.throw(__("Please enter Item Code to get batch no")); + } else if (doc.doctype == "Purchase Receipt" || + (doc.doctype == "Purchase Invoice" && doc.update_stock)) { + + return { + filters: {'item': item.item_code} + } + } else { + filters = { + 'item_code': item.item_code, + 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), + } + if(item.warehouse) filters["warehouse"] = item.warehouse + + return { + query : "erpnext.controllers.queries.get_batch_no", + filters: filters + } + } + }, }); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 1fcf334717..6233f05d8b 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -73,34 +73,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ return me.set_query_for_batch(doc, cdt, cdn) }); } - - if(this.frm.fields_dict["items"].grid.get_field('batch_no')) { - this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) { - return me.set_query_for_batch(doc, cdt, cdn) - }); - } - }, - - set_query_for_batch: function(doc, cdt, cdn) { - // Show item's batches in the dropdown of batch no - - var me = this; - var item = frappe.get_doc(cdt, cdn); - - if(!item.item_code) { - frappe.throw(__("Please enter Item Code to get batch no")); - } else { - filters = { - 'item_code': item.item_code, - 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), - } - if(item.warehouse) filters["warehouse"] = item.warehouse - - return { - query : "erpnext.controllers.queries.get_batch_no", - filters: filters - } - } }, refresh: function() { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 5c97e7cd63..b4cfcaab43 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -145,17 +145,6 @@ cur_frm.fields_dict['items'].grid.get_field('project').get_query = function(doc, } } -cur_frm.fields_dict['items'].grid.get_field('batch_no').get_query= function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if(d.item_code) { - return { - filters: {'item': d.item_code} - } - } - else - msgprint(__("Please enter Item Code.")); -} - cur_frm.cscript.select_print_heading = function(doc, cdt, cdn) { if(doc.select_print_heading) cur_frm.pformat.print_heading = doc.select_print_heading; From 2a0d65073585f35657dff3718a6b9e4e4d7ae480 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 7 Jun 2017 11:29:27 +0530 Subject: [PATCH 02/14] fix in the report (#9167) --- .../student_and_guardian_contact_details.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/schools/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py b/erpnext/schools/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py index 2e59d61245..f191022f20 100644 --- a/erpnext/schools/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py +++ b/erpnext/schools/report/student_and_guardian_contact_details/student_and_guardian_contact_details.py @@ -22,13 +22,14 @@ def execute(filters=None): if not student_list: return columns, [] + group_roll_no_map = get_student_roll_no(academic_year, program, student_batch_name) student_map = get_student_details(student_list) guardian_map = get_guardian_map(student_list) for d in program_enrollments: student_details = student_map.get(d.student) - row = [d.student, d.student_name, student_details.get("student_mobile_number"), student_details.get("student_email_id"), - student_details.get("address")] + row = [group_roll_no_map.get(d.student), d.student, d.student_name, student_details.get("student_mobile_number"),\ + student_details.get("student_email_id"), student_details.get("address")] student_guardians = guardian_map.get(d.student) @@ -44,7 +45,8 @@ def execute(filters=None): def get_columns(): - columns = [ + columns = [ + _(" Group Roll No") + "::60", _("Student ID") + ":Link/Student:90", _("Student Name") + "::150", _("Student Mobile No.") + "::110", @@ -93,4 +95,13 @@ def get_guardian_map(student_list): guardian["email_address"] = guardian_email_id.get(guardian.guardian) guardian_map.setdefault(guardian.parent, []).append(guardian) - return guardian_map \ No newline at end of file + return guardian_map + +def get_student_roll_no(academic_year, program, batch): + student_group = frappe.get_all("Student Group", + filters={"academic_year":academic_year, "program":program, "batch":batch}) + if student_group: + roll_no_dict = dict(frappe.db.sql('''select student, group_roll_number from `tabStudent Group Student` where parent=%s''', + (student_group[0].name))) + return roll_no_dict + return {} From ce6e621c18f5c08c018c5e078d5f64ddece1dcef Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 7 Jun 2017 11:52:04 +0530 Subject: [PATCH 03/14] [fix] Not able to cancel the invoice if it's linked with the timesheet (#9157) --- .../doctype/sales_invoice/sales_invoice.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4b1837d4d7..ab49fc084b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -243,7 +243,6 @@ class SalesInvoice(SellingController): (not self.project and not data.sales_invoice) or \ (not sales_invoice and data.sales_invoice == self.name): data.sales_invoice = sales_invoice - if self.project: return def on_update(self): self.set_paid_amount() @@ -483,13 +482,14 @@ class SalesInvoice(SellingController): self.set('packed_items', []) def set_billing_hours_and_amount(self): - for timesheet in self.timesheets: - ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet) - if not timesheet.billing_hours and ts_doc.total_billable_hours: - timesheet.billing_hours = ts_doc.total_billable_hours + if not self.project: + for timesheet in self.timesheets: + ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet) + if not timesheet.billing_hours and ts_doc.total_billable_hours: + timesheet.billing_hours = ts_doc.total_billable_hours - if not timesheet.billing_amount and ts_doc.total_billable_amount: - timesheet.billing_amount = ts_doc.total_billable_amount + if not timesheet.billing_amount and ts_doc.total_billable_amount: + timesheet.billing_amount = ts_doc.total_billable_amount def update_timesheet_billing_for_project(self): if not self.timesheets and self.project: From 39adfb52f33f279901bf7945f717a7e1cb982143 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 7 Jun 2017 11:52:36 +0530 Subject: [PATCH 04/14] Naming in the assessment plan (#9153) --- .../schools/doctype/assessment_plan/assessment_plan.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/schools/doctype/assessment_plan/assessment_plan.json b/erpnext/schools/doctype/assessment_plan/assessment_plan.json index 697a0d4edc..e2ac3219eb 100644 --- a/erpnext/schools/doctype/assessment_plan/assessment_plan.json +++ b/erpnext/schools/doctype/assessment_plan/assessment_plan.json @@ -3,7 +3,7 @@ "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, - "autoname": "field:assessment_name", + "autoname": "ASP.#####", "beta": 0, "creation": "2015-11-12 16:34:34.658092", "custom": 0, @@ -38,7 +38,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -633,7 +633,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-05-02 12:54:11.991616", + "modified": "2017-06-05 23:40:30.434741", "modified_by": "Administrator", "module": "Schools", "name": "Assessment Plan", @@ -667,6 +667,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "title_field": "assessment_name", "track_changes": 0, "track_seen": 0 } \ No newline at end of file From 214e6906e5b7f25188446a39d72ce10493b6aa67 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 7 Jun 2017 11:53:41 +0530 Subject: [PATCH 05/14] Addition of field 'group based on' in the student attendance tool (#9152) --- .../student_attendance_tool.js | 10 ++++++ .../student_attendance_tool.json | 35 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js index 6f8c6cd7da..8fe8f8c442 100644 --- a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js +++ b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.js @@ -3,6 +3,16 @@ frappe.provide("schools") frappe.ui.form.on('Student Attendance Tool', { + onload: function(frm) { + frm.set_query("student_group", function() { + return { + "filters": { + "group_based_on": frm.doc.group_based_on + } + }; + }); + }, + refresh: function(frm) { frm.disable_save(); }, diff --git a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.json b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.json index 291e027adf..265ac8ca0b 100644 --- a/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.json +++ b/erpnext/schools/doctype/student_attendance_tool/student_attendance_tool.json @@ -44,6 +44,39 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Batch", + "depends_on": "eval:doc.based_on == \"Student Group\"", + "fieldname": "group_based_on", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Group Based On", + "length": 0, + "no_copy": 0, + "options": "Batch\nCourse\nActivity", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -240,7 +273,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-05-01 15:09:55.740005", + "modified": "2017-06-05 23:16:43.127216", "modified_by": "Administrator", "module": "Schools", "name": "Student Attendance Tool", From db8363e9ebfa28093ef6361dc26aa8fee4f8e95a Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Wed, 7 Jun 2017 11:54:26 +0530 Subject: [PATCH 06/14] [minor] check serial no filedtype before updating (#9126) * Update serial_no.py Takes care of Different FieldTypes * [minor] check serial no filedtype before updating --- erpnext/stock/doctype/serial_no/serial_no.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 03d4d73a98..8420c9b4da 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -160,7 +160,7 @@ class SerialNo(StockController): def after_rename(self, old, new, merge=False): """rename serial_no text fields""" for dt in frappe.db.sql("""select parent from tabDocField - where fieldname='serial_no' and fieldtype='Text'"""): + where fieldname='serial_no' and fieldtype in ('Text', 'Small Text')"""): for item in frappe.db.sql("""select name, serial_no from `tab%s` where serial_no like '%%%s%%'""" % (dt[0], frappe.db.escape(old))): From 3a18dea8a4331cddcf719c2e5e4ad4e3e1b55452 Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Wed, 7 Jun 2017 11:55:25 +0530 Subject: [PATCH 07/14] [fix][multiselect] don't show completed PRECs for a PINV (#9125) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index b48fb20d6f..cada95f2f6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -78,7 +78,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ }, get_query_filters: { docstatus: 1, - status: ["!=", "Closed"], + status: ["not in", ["Closed", "Completed"]], company: me.frm.doc.company, is_return: 0 } From b76b4f40a022a40c5d5a049d482f228d9463ca17 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 7 Jun 2017 11:56:26 +0530 Subject: [PATCH 08/14] Added mode of transport in program enrollment (#9124) --- .../program_enrollment.json | 125 +++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/erpnext/schools/doctype/program_enrollment/program_enrollment.json b/erpnext/schools/doctype/program_enrollment/program_enrollment.json index a07b3eec05..f2bc416c8e 100644 --- a/erpnext/schools/doctype/program_enrollment/program_enrollment.json +++ b/erpnext/schools/doctype/program_enrollment/program_enrollment.json @@ -325,8 +325,129 @@ "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "vehicle_no", + "columns": 0, + "fieldname": "transportation", + "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": "Transportation", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, "collapsible": 0, "columns": 0, + "fieldname": "mode_of_transportation", + "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": "Mode of Transportation", + "length": 0, + "no_copy": 0, + "options": "\nSchool Bus\nPublic Transport\nSelf-Driving Vehicle\nPick/Drop by Guardian", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "vehicle_no", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Vehicle/Bus Number", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, "fieldname": "enrolled_courses", "fieldtype": "Section Break", "hidden": 0, @@ -416,7 +537,7 @@ "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "columns": 0, "fieldname": "section_break_7", "fieldtype": "Section Break", @@ -547,7 +668,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-04-24 18:22:43.567607", + "modified": "2017-06-02 15:29:05.812335", "modified_by": "Administrator", "module": "Schools", "name": "Program Enrollment", From 17378e8a29142a0ec32c3c088e52080da32c062c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 7 Jun 2017 11:56:53 +0530 Subject: [PATCH 09/14] Added a new column for Credit/Debit Note in AR/AP report (#9123) --- .../accounts_receivable.py | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 62d0d5a167..9906893254 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -33,7 +33,9 @@ class ReceivablePayableReport(object): if args.get("party_type") == "Supplier": columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] - for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"): + credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note" + + for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"): columns.append({ "label": label, "fieldtype": "Currency", @@ -95,13 +97,14 @@ class ReceivablePayableReport(object): self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company') company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency") + + return_entries = self.get_return_entries(args.get("party_type")) data = [] for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")): if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers): - outstanding_amount = flt(self.get_outstanding_amount(gle, - self.filters.report_date, dr_or_cr), currency_precision) - + outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle, + self.filters.report_date, dr_or_cr, return_entries, currency_precision) if abs(outstanding_amount) > 0.1/10**currency_precision: row = [gle.posting_date, gle.party] @@ -123,8 +126,8 @@ class ReceivablePayableReport(object): # invoiced and paid amounts invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0 - paid_amt = invoiced_amount - outstanding_amount - row += [invoiced_amount, paid_amt, outstanding_amount] + paid_amt = invoiced_amount - outstanding_amount - credit_note_amount + row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount] # ageing data entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date @@ -132,7 +135,8 @@ class ReceivablePayableReport(object): cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount) # issue 6371-Ageing buckets should not have amounts if due date is not reached - if self.filters.ageing_based_on == "Due Date" and getdate(due_date) > getdate(self.filters.report_date): + if self.filters.ageing_based_on == "Due Date" \ + and getdate(due_date) > getdate(self.filters.report_date): row[-1]=row[-2]=row[-3]=row[-4]=0 if self.filters.get(scrub(args.get("party_type"))): @@ -175,14 +179,28 @@ class ReceivablePayableReport(object): # entries adjusted with future vouchers ((gle.against_voucher_type, gle.against_voucher) in future_vouchers) ) + + def get_return_entries(self, party_type): + doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice" + return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})] - def get_outstanding_amount(self, gle, report_date, dr_or_cr): - payment_amount = 0.0 + def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision): + payment_amount, credit_note_amount = 0.0, 0.0 + reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" + for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): if getdate(e.posting_date) <= report_date and e.name!=gle.name: - payment_amount += (flt(e.credit if gle.party_type == "Customer" else e.debit) - flt(e.get(dr_or_cr))) - - return flt(gle.get(dr_or_cr)) - flt(gle.credit if gle.party_type == "Customer" else gle.debit) - payment_amount + amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr)) + if e.voucher_no not in return_entries: + payment_amount += amount + else: + credit_note_amount += amount + + outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \ + - payment_amount - credit_note_amount), currency_precision) + credit_note_amount = flt(credit_note_amount, currency_precision) + + return outstanding_amount, credit_note_amount def get_party_name(self, party_type, party_name): return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or "" From ba7ed4626c03f50b0d13614ba904392dff40c251 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 7 Jun 2017 11:57:11 +0530 Subject: [PATCH 10/14] fetch query for assessment group (#9120) --- .../doctype/assessment_plan/assessment_plan.js | 12 +++++++++++- .../doctype/assessment_plan/assessment_plan.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/schools/doctype/assessment_plan/assessment_plan.js b/erpnext/schools/doctype/assessment_plan/assessment_plan.js index 3c9ab80d2d..abdb0c812a 100644 --- a/erpnext/schools/doctype/assessment_plan/assessment_plan.js +++ b/erpnext/schools/doctype/assessment_plan/assessment_plan.js @@ -6,7 +6,17 @@ cur_frm.add_fetch("examiner", "instructor_name", "examiner_name"); cur_frm.add_fetch("supervisor", "instructor_name", "supervisor_name"); frappe.ui.form.on("Assessment Plan", { - refresh: function(frm) { + onload: function(frm) { + frm.set_query("assessment_group", function(doc, cdt, cdn) { + return{ + filters: { + 'is_group': 0 + } + } + }); + }, + + refresh: function(frm) { if (frm.doc.docstatus == 1) { frm.add_custom_button(__("Assessment Result"), function() { frappe.route_options = { diff --git a/erpnext/schools/doctype/assessment_plan/assessment_plan.py b/erpnext/schools/doctype/assessment_plan/assessment_plan.py index 7f83c0176b..f988886fca 100644 --- a/erpnext/schools/doctype/assessment_plan/assessment_plan.py +++ b/erpnext/schools/doctype/assessment_plan/assessment_plan.py @@ -36,4 +36,4 @@ class AssessmentPlan(Document): for d in self.assessment_criteria: max_score += d.maximum_score if self.maximum_assessment_score != max_score: - frappe.throw(_("Sum of Scores of Assessment Criteria needs to be {0}.".format(self.maximum_assessment_score))) \ No newline at end of file + frappe.throw(_("Sum of Scores of Assessment Criteria needs to be {0}.".format(self.maximum_assessment_score))) From 99b734bfd7930a624ef4b6af63bc24907e31e3bd Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 7 Jun 2017 07:32:07 +0100 Subject: [PATCH 11/14] Fix #4587: Status does not display "Pending" in report filter (#9104) * adds Material Request to `status_map` * updates eval condition for Partially Ordered in Material Request map * changes material_request doctype to include "pending", "ordered", "partially ordered", "issued", "transferred" as options * adds more options to `validate_status` * adds `set_status` just before saving * adds `check_for_closed_status` in `before_cancel` * adds patch to convert status to material request specific status * adds stricter status update conditions * changes `update_status` to me `set_status` * adds checker such that draft status can only change to pending * renames `check_draft_status` to `status_can_change` * adds Cancelled to Material Request map * makes `status_can_change` block any attempt to change a cancelled document * adds more test cases * updates what `set_status` checks for before adding comment * adds patch to rename the present material request status --- erpnext/controllers/status_updater.py | 13 ++- erpnext/patches.txt | 1 + ...ems_in_status_field_of_material_request.py | 25 ++++++ .../material_request/material_request.json | 28 +++++- .../material_request/material_request.py | 47 ++++++++-- .../material_request/test_material_request.py | 88 +++++++++++++++++++ 6 files changed, 191 insertions(+), 11 deletions(-) create mode 100644 erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 42327b94ac..2f54fc0175 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -85,6 +85,16 @@ status_map = { ["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], + ], + "Material Request": [ + ["Draft", None], + ["Stopped", "eval:self.status == 'Stopped'"], + ["Cancelled", "eval:self.docstatus == 2"], + ["Pending", "eval:self.status != 'Stopped' and self.per_ordered == 0 and self.docstatus == 1"], + ["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"], + ["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"], + ["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"], + ["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"] ] } @@ -127,7 +137,8 @@ class StatusUpdater(Document): self.status = s[0] break - if self.status != _status and self.status not in ("Submitted", "Cancelled"): + if self.status != _status and self.status not in ("Cancelled", "Partially Ordered", + "Ordered", "Issued", "Transferred"): self.add_comment("Label", _(self.status)) if update: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 853efa1cc3..0777ab744e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -397,3 +397,4 @@ erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice erpnext.patches.v8_0.delete_schools_depricated_doctypes +erpnext.patches.v8_0.rename_items_in_status_field_of_material_request diff --git a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py new file mode 100644 index 0000000000..5ad862a436 --- /dev/null +++ b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.sql( + """ + UPDATE `tabMaterial Request` + SET status = CASE + WHEN docstatus = 2 THEN 'Cancelled' + WHEN docstatus = 0 THEN 'Draft' + ELSE CASE + WHEN status = 'Stopped' THEN 'Stopped' + WHEN status != 'Stopped' AND per_ordered = 0 THEN 'Pending' + WHEN per_ordered < 100 AND per_ordered > 0 AND status != 'Stopped' + THEN 'Partially Ordered' + WHEN per_ordered = 100 AND material_request_type = 'Purchase' + AND status != 'Stopped' THEN 'Ordered' + WHEN per_ordered = 100 AND material_request_type = 'Material Transfer' + AND status != 'Stopped' THEN 'Transferred' + WHEN per_ordered = 100 AND material_request_type = 'Material Issue' + AND status != 'Stopped' THEN 'Issued' + END + END + """ + ) \ No newline at end of file diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 2ab0907fa9..fc174a46e0 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "autoname": "naming_series:", @@ -12,6 +13,7 @@ "editable_grid": 0, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -41,6 +43,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -71,6 +74,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -100,6 +104,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -127,6 +132,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -158,6 +164,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -191,6 +198,7 @@ "width": "150px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -225,6 +233,7 @@ "width": "150px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -255,6 +264,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -286,6 +296,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -316,6 +327,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -344,6 +356,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -377,6 +390,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -407,6 +421,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -425,7 +440,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled", + "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -440,6 +455,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -471,6 +487,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -500,6 +517,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -531,6 +549,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -560,6 +579,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -591,6 +611,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -622,6 +643,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -652,19 +674,19 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-ticket", "idx": 70, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-02-20 13:29:56.743544", + "modified": "2017-05-31 15:06:44.611826", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 82c4c19fdf..65263a0694 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -70,7 +70,9 @@ class MaterialRequest(BuyingController): self.status = "Draft" from erpnext.controllers.status_updater import validate_status - validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled"]) + validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled", "Pending", + "Partially Ordered", "Ordered", "Issued", "Transferred"] + ) validate_for_items(self) @@ -91,9 +93,20 @@ class MaterialRequest(BuyingController): self.title = ', '.join(items) def on_submit(self): - frappe.db.set(self, 'status', 'Submitted') + # frappe.db.set(self, 'status', 'Submitted') self.update_requested_qty() + def before_save(self): + self.set_status(update=True) + + def before_submit(self): + self.set_status(update=True) + + def before_cancel(self): + # if MRQ is already closed, no point saving the document + check_for_closed_status(self.doctype, self.name) + self.set_status(update=True, status='Cancelled') + def check_modified_date(self): mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""", self.name) @@ -105,16 +118,36 @@ class MaterialRequest(BuyingController): def update_status(self, status): self.check_modified_date() - frappe.db.set(self, 'status', cstr(status)) + self.status_can_change(status) + self.set_status(update=True, status=status) self.update_requested_qty() + def status_can_change(self, status): + """ + validates that `status` is acceptable for the present controller status + and throws an Exception if otherwise. + """ + if self.status and self.status == 'Cancelled': + # cancelled documents cannot change + if status != self.status: + frappe.throw( + _("{0} {1} is cancelled so the action cannot be completed"). + format(_(self.doctype), self.name), + frappe.InvalidStatusError + ) + + elif self.status and self.status == 'Draft': + # draft document to pending only + if status != 'Pending': + frappe.throw( + _("{0} {1} has not been submitted so the action cannot be completed"). + format(_(self.doctype), self.name), + frappe.InvalidStatusError + ) + def on_cancel(self): - check_for_closed_status(self.doctype, self.name) - self.update_requested_qty() - frappe.db.set(self,'status','Cancelled') - def update_completed_qty(self, mr_items=None, update_modified=True): if self.material_request_type == "Purchase": return diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 8f43acdc7b..c3a2137412 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -98,6 +98,94 @@ class TestMaterialRequest(unittest.TestCase): se.insert() se.submit() + def test_cannot_stop_cancelled_material_request(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + + mr.load_from_db() + mr.cancel() + self.assertRaises(frappe.ValidationError, mr.update_status, 'Stopped') + + def test_mr_changes_from_stopped_to_pending_after_reopen(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + self.assertEqual('Pending', mr.status) + + mr.update_status('Stopped') + self.assertEqual('Stopped', mr.status) + + mr.update_status('Submitted') + self.assertEqual('Pending', mr.status) + + def test_cannot_submit_cancelled_mr(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + mr.load_from_db() + mr.cancel() + self.assertRaises(frappe.ValidationError, mr.submit) + + def test_mr_changes_from_pending_to_cancelled_after_cancel(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + mr.cancel() + self.assertEqual('Cancelled', mr.status) + + def test_cannot_change_cancelled_mr(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + mr.load_from_db() + mr.cancel() + + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Draft') + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped') + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Ordered') + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Issued') + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Transferred') + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Pending') + + def test_cannot_submit_deleted_material_request(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.delete() + + self.assertRaises(frappe.ValidationError, mr.submit) + + def test_cannot_delete_submitted_mr(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + + self.assertRaises(frappe.ValidationError, mr.delete) + + def test_stopped_mr_changes_to_pending_after_reopen(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + mr.load_from_db() + + mr.update_status('Stopped') + mr.update_status('Submitted') + self.assertEqual(mr.status, 'Pending') + + def test_pending_mr_changes_to_stopped_after_stop(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + mr.submit() + mr.load_from_db() + + mr.update_status('Stopped') + self.assertEqual(mr.status, 'Stopped') + + def test_cannot_stop_unsubmitted_mr(self): + mr = frappe.copy_doc(test_records[0]) + mr.insert() + self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped') + def test_completed_qty_for_purchase(self): existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") From d2be55b2e028186a88655a8b174504317125d05c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 7 Jun 2017 12:04:01 +0530 Subject: [PATCH 12/14] [Fix] Orders not creating from the POS for new customer if customer name is based on naming series (#8933) * [Fix] Orders not creating from the POS for new customer if customer name is based on naming series * [fix] Disabled price field in POS, if Allow User to Edit Rate field is disabled in POS profile * added customer name in the search bar of the customer * search customer by phone number and email id in the POS --- erpnext/accounts/doctype/sales_invoice/pos.py | 87 +++++++++--- erpnext/accounts/page/pos/pos.js | 127 +++++++++++------- .../point_of_sale/point_of_sale.json | 4 +- erpnext/patches.txt | 1 + .../patches/v8_0/update_customer_pos_id.py | 9 ++ .../public/js/controllers/taxes_and_totals.js | 2 +- erpnext/public/js/pos/pos.html | 2 +- erpnext/public/js/pos/pos_selected_item.html | 2 +- .../selling/doctype/customer/customer.json | 70 +++++++++- 9 files changed, 225 insertions(+), 79 deletions(-) create mode 100644 erpnext/patches/v8_0/update_customer_pos_id.py diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index a899cde25d..0f0569a104 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -35,6 +35,7 @@ def get_pos_data(): 'item_groups': get_item_groups(pos_profile), 'customers': customers, 'address': get_customers_address(customers), + 'contacts': get_contacts(customers), 'serial_no_data': get_serial_no_data(pos_profile, doc.company), 'batch_no_data': get_batch_no_data(), 'tax_data': get_item_tax_data(), @@ -160,7 +161,7 @@ def get_item_groups(pos_profile): item_group_dict[data.name] = [data.lft, data.rgt] return item_group_dict -def get_customers_list(pos_profile): +def get_customers_list(pos_profile={}): cond = "1=1" customer_groups = [] if pos_profile.get('customer_groups'): @@ -170,7 +171,7 @@ def get_customers_list(pos_profile): cond = "customer_group in (%s)"%(', '.join(['%s']*len(customer_groups))) return frappe.db.sql(""" select name, customer_name, customer_group, - territory from tabCustomer where disabled = 0 + territory, customer_pos_id from tabCustomer where disabled = 0 and {cond}""".format(cond=cond), tuple(customer_groups), as_dict=1) or {} def get_customers_address(customers): @@ -183,13 +184,29 @@ def get_customers_address(customers): email_id, phone, fax, pincode from `tabAddress` where is_primary_address =1 and name in (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s and parenttype = 'Address')""", data.name, as_dict=1) - if address: - address_data = address[0] - address_data.update({'full_name': data.customer_name}) - customer_address[data.name] = address_data + address_data = {} + if address: address_data = address[0] + + address_data.update({'full_name': data.customer_name, 'customer_pos_id': data.customer_pos_id}) + customer_address[data.name] = address_data return customer_address +def get_contacts(customers): + customer_contact = {} + if isinstance(customers, basestring): + customers = [frappe._dict({'name': customers})] + + for data in customers: + contact = frappe.db.sql(""" select email_id, phone from `tabContact` + where is_primary_contact =1 and name in + (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s + and parenttype = 'Contact')""", data.name, as_dict=1) + if contact: + customer_contact[data.name] = contact[0] + + return customer_contact + def get_child_nodes(group_type, root): lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"]) return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where @@ -294,7 +311,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): if isinstance(customers_list, basestring): customers_list = json.loads(customers_list) - customers = make_customer_and_address(customers_list) + customers_list = make_customer_and_address(customers_list) name_list = [] for docs in doc_list: for name, doc in docs.items(): @@ -303,6 +320,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): si_doc = frappe.new_doc('Sales Invoice') si_doc.offline_pos_name = name si_doc.update(doc) + si_doc.customer = get_customer_id(doc) si_doc.due_date = doc.get('posting_date') submit_invoice(si_doc, name, doc) name_list.append(name) @@ -310,30 +328,54 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): name_list.append(name) email_queue = make_email_queue(email_queue_list) + customers = get_customers_list() return { 'invoice': name_list, 'email_queue': email_queue, - 'customers': customers + 'customers': customers_list, + 'synced_customers_list': customers, + 'synced_address': get_customers_address(customers), + 'synced_contacts': get_contacts(customers) } def validate_records(doc): validate_item(doc) -def make_customer_and_address(customers): - customer_list = [] - for name, data in customers.items(): - if not frappe.db.exists('Customer', name): - name = add_customer(name) - data = json.loads(data) - make_contact(data, name) - make_address(data, name) - customer_list.append(name) - frappe.db.commit() - return customer_list +def get_customer_id(doc, customer=None): + cust_id = None + if doc.get('customer_pos_id'): + cust_id = frappe.db.get_value('Customer', + {'customer_pos_id': doc.get('customer_pos_id')}, 'name') -def add_customer(name): + if not cust_id: + customer = customer or doc.get('customer') + if frappe.db.exists('Customer', customer): + cust_id = customer + else: + cust_id = add_customer(doc) + + return cust_id + +def make_customer_and_address(customers): + customers_list = [] + for customer, data in customers.items(): + data = json.loads(data) + cust_id = get_customer_id(data, customer) + if not cust_id: + cust_id = add_customer(data) + else: + frappe.db.set_value("Customer", cust_id, "customer_name", data.get('full_name')) + + make_contact(data, cust_id) + make_address(data, cust_id) + customers_list.append(customer) + frappe.db.commit() + return customers_list + +def add_customer(data): customer_doc = frappe.new_doc('Customer') - customer_doc.customer_name = name + customer_doc.customer_name = data.get('full_name') or data.get('customer') + customer_doc.customer_pos_id = data.get('customer_pos_id') customer_doc.customer_type = 'Company' customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory') @@ -348,6 +390,7 @@ def make_contact(args,customer): {'link_doctype': 'Customer', 'link_name': customer, 'parenttype': 'Contact'}, 'parent') args = { + 'first_name': args.get('full_name'), 'email_id': args.get('email_id'), 'phone': args.get('phone') } @@ -357,8 +400,8 @@ def make_contact(args,customer): doc = frappe.get_doc('Contact', name) doc.update(args) + doc.is_primary_contact = 1 if not name: - doc.first_name = customer doc.append('links',{ 'link_doctype': 'Customer', 'link_name': customer diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 223e915fe3..44941102f1 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -20,6 +20,7 @@ frappe.pages['pos'].refresh = function (wrapper) { erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ init: function (wrapper) { this.page_len = 20; + this.freeze = false; this.page = wrapper.page; this.wrapper = $(wrapper).find('.page-content'); this.set_indicator(); @@ -72,6 +73,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ onload: function () { var me = this; this.get_data_from_server(function () { + me.make_control(); me.create_new(); }); }, @@ -96,7 +98,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.page.add_menu_item(__("Sync Master Data"), function () { me.get_data_from_server(function () { me.load_data(false); - me.make_customer(); me.make_item_list(); me.set_missing_values(); }) @@ -311,6 +312,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.serial_no_data = r.message.serial_no_data; this.batch_no_data = r.message.batch_no_data; this.tax_data = r.message.tax_data; + this.contacts = r.message.contacts; this.address = r.message.address || {}; this.price_list_data = r.message.price_list_data; this.bin_data = r.message.bin_data; @@ -320,7 +322,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.default_customer = r.message.default_customer || null; this.print_settings = locals[":Print Settings"]["Print Settings"]; this.letter_head = (this.pos_profile_data.length > 0) ? frappe.boot.letter_heads[this.pos_profile_data[letter_head]] : {}; - this.make_control() }, save_previous_entry: function () { @@ -403,6 +404,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.frm = {} this.frm.doc = this.doc this.set_transaction_defaults("Customer"); + this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, this.wrapper.html(frappe.render_template("pos", this.frm.doc)); this.make_search(); this.make_customer(); @@ -707,15 +709,28 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ autoFirst: true, list: [], filter: function (item, input) { - var value = item.value.toLowerCase(); - if (value.indexOf('is_action') !== -1 || - value.indexOf(input.toLowerCase()) !== -1) { + if (item.value.includes('is_action')) { return true; } + + input = input.toLowerCase(); + item = this.get_item(item.value); + var searchtext = + Object.keys(item) + .filter(key => ['customer_name', 'customer_group', 'value', 'label', 'email_id', 'phone'].includes(key)) + .map(key => item[key]) + .join(" ") + .toLowerCase(); + + return searchtext.includes(input) }, item: function (item, input) { - var d = item; + var d = this.get_item(item.value); var html = "" + __(d.label || d.value) + ""; + if(d.customer_name) { + html += '
' + __(d.customer_name) + ''; + } + return $('
  • ') .data('item.autocomplete', d) .html('

    ' + html + '

    ') @@ -723,28 +738,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } }); - this.customers_mapper = this.customers.map(function (c) { - return { - label: c.name, - value: c.name, - customer_group: c.customer_group, - territory: c.territory - } - }); - - this.customers_mapper.push({ - label: "" - + " " - + __("Create a new Customer") - + "", - value: 'is_action', - action: me.add_customer - }); + this.prepare_customer_mapper() this.autocomplete_customers(); this.party_field.$input .on('input', function (e) { - me.party_field.awesomeplete.list = this.customers_mapper; + me.party_field.awesomeplete.list = me.customers_mapper; }) .on('awesomplete-select', function (e) { var customer = me.party_field.awesomeplete @@ -784,6 +783,32 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); }, + prepare_customer_mapper: function() { + var me = this; + + this.customers_mapper = this.customers.map(function (c) { + contact = me.contacts[c.name]; + return { + label: c.name, + value: c.name, + customer_name: c.customer_name, + customer_group: c.customer_group, + territory: c.territory, + phone: contact ? contact["phone"] : '', + email_id: contact ? contact["email_id"] : '' + } + }); + + this.customers_mapper.push({ + label: "" + + " " + + __("Create a new Customer") + + "", + value: 'is_action', + action: me.add_customer + }); + }, + autocomplete_customers: function() { this.party_field.awesomeplete.list = this.customers_mapper; }, @@ -871,10 +896,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ "label": __("ZIP Code"), "fieldname": "pincode", "fieldtype": "Data" + }, + { + "label": __("Customer POS Id"), + "fieldname": "customer_pos_id", + "fieldtype": "Data", + "hidden": 1 } ] }) - this.customer_doc.show() this.render_address_data() @@ -887,12 +917,19 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ render_address_data: function() { var me = this; - this.address_data = this.address[this.frm.doc.customer]; - this.customer_doc.set_values(this.address_data) + this.address_data = this.address[this.frm.doc.customer] || {}; + if(!this.address_data.email_id || !this.address_data.phone) { + this.address_data = this.contacts[this.frm.doc.customer]; + } + this.customer_doc.set_values(this.address_data) if(!this.customer_doc.fields_dict.full_name.$input.val()) { this.customer_doc.set_value("full_name", this.frm.doc.customer) } + + if(!this.customer_doc.fields_dict.customer_pos_id.value) { + this.customer_doc.set_value("customer_pos_id", $.now()) + } }, get_address_from_localstorage: function() { @@ -902,6 +939,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ make_offline_customer: function(new_customer) { this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name; + this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value; this.customer_details = this.get_customers_details(); this.customer_details[this.frm.doc.customer] = this.get_prompt_details(); this.party_field.$input.val(this.frm.doc.customer); @@ -923,12 +961,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); } - this.address[this.frm.doc.customer] = this.customer_doc.get_values(); + this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details()) }, get_prompt_details: function() { this.prompt_details = this.customer_doc.get_values(); this.prompt_details['country'] = this.pos_profile_data.country; + this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value; return JSON.stringify(this.prompt_details) }, @@ -942,26 +981,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.numeric_keypad.show(); }, - get_customers: function (key) { - var me = this; - key = key.toLowerCase().trim() - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*')) - - if (key) { - return $.grep(this.customers, function (data) { - if (reg.test(data.name.toLowerCase()) - || reg.test(data.customer_name.toLowerCase()) - || (data.customer_group && reg.test(data.customer_group.toLowerCase()))) { - return data - } - }) - } else { - customers = this.customers.sort(function (a, b) { return a.idx < b.idx }) - return customers.slice(0, 20) - } - }, - make_item_list: function () { var me = this; if (!this.price_list) { @@ -1180,6 +1199,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child_doc = this.get_child_item(this.item_code); $(this.wrapper).find('.selected-item').empty(); if(this.child_doc.length) { + this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) $(this.wrapper).find('.selected-item').html(this.selected_row) } @@ -1388,7 +1408,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ actual_qty: me.actual_qty_dict[d.item_code] || 0.0, projected_qty: d.projected_qty, rate: format_currency(d.rate, me.frm.doc.currency), - enabled: me.pos_profile_data["allow_user_to_edit_rate"] ? true : false, amount: format_currency(d.amount, me.frm.doc.currency), selected_class: (me.item_code == d.item_code) ? "active" : "" })).appendTo($items); @@ -1608,8 +1627,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.si_docs = this.get_submitted_invoice() || []; this.email_queue_list = this.get_email_queue() || {}; this.customers_list = this.get_customers_details() || {}; + if(this.customer_doc) { + this.freeze = this.customer_doc.display + } - if (this.si_docs.length || this.email_queue_list || this.customers_list) { + if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) { frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", args: { @@ -1619,12 +1641,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, callback: function (r) { if (r.message) { + me.customers = r.message.synced_customers_list; + me.address = r.message.synced_address; + me.contacts = r.message.synced_contacts; me.removed_items = r.message.invoice; me.removed_email = r.message.email_queue me.removed_customers = r.message.customers me.remove_doc_from_localstorage(); me.remove_email_queue_from_localstorage(); me.remove_customer_from_localstorage(); + me.prepare_customer_mapper() + me.autocomplete_customers() } } }) diff --git a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json index b413321bfd..28c853cc48 100644 --- a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json +++ b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "html": "\n\n

    \n\t{{ company }}
    \n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
    \n

    \n

    \n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
    \n

    \n\n
    \n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
    {{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
    \n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
    @ {{ format_currency(item.rate, currency) }}
    {{ format_currency(item.amount, currency) }}
    \n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
    \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
    \n\n\n
    \n

    {{ terms }}

    \n

    {{ __(\"Thank you, please visit again.\") }}

    ", + "html": "\n\n

    \n\t{{ company }}
    \n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
    \n

    \n

    \n\t{{ __(\"Customer\") }}: {{ customer }}
    \n

    \n\n

    \n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
    \n

    \n\n
    \n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in items %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
    {{ __(\"Item\") }}{{ __(\"Qty\") }}{{ __(\"Amount\") }}
    \n\t\t\t\t{{ item.item_name }}\n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }}
    @ {{ format_currency(item.rate, currency) }}
    {{ format_currency(item.amount, currency) }}
    \n\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endif %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
    \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ row.description }}\n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t
    \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t
    \n\n\n
    \n

    {{ terms }}

    \n

    {{ __(\"Thank you, please visit again.\") }}

    ", "idx": 0, "line_breaks": 0, - "modified": "2017-04-19 13:28:05.129504", + "modified": "2017-05-19 14:36:04.740728", "modified_by": "Administrator", "module": "Accounts", "name": "Point of Sale", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0777ab744e..7dc7e3cbc6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -397,4 +397,5 @@ erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice erpnext.patches.v8_0.delete_schools_depricated_doctypes +erpnext.patches.v8_0.update_customer_pos_id erpnext.patches.v8_0.rename_items_in_status_field_of_material_request diff --git a/erpnext/patches/v8_0/update_customer_pos_id.py b/erpnext/patches/v8_0/update_customer_pos_id.py new file mode 100644 index 0000000000..a772ae90c5 --- /dev/null +++ b/erpnext/patches/v8_0/update_customer_pos_id.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doctype("Customer") + frappe.db.sql(""" update `tabCustomer` set customer_pos_id = name """) \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index dc1db3554b..1437ee929e 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -606,7 +606,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc.change_amount = 0.0; this.frm.doc.base_change_amount = 0.0; if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) { - var payment_types = $.map(cur_frm.doc.payments, function(d) { return d.type }); + var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type }); if (in_list(payment_types, 'Cash')) { this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total + this.frm.doc.write_off_amount, precision("change_amount")); diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html index 6065e603c7..485a94584e 100644 --- a/erpnext/public/js/pos/pos.html +++ b/erpnext/public/js/pos/pos.html @@ -73,7 +73,7 @@ {% for(var j=i*3; j <(i+1)*3; j++) { %} {% } %} - + {% } %}
    diff --git a/erpnext/public/js/pos/pos_selected_item.html b/erpnext/public/js/pos/pos_selected_item.html index a4bc49dfba..64f8c16522 100644 --- a/erpnext/public/js/pos/pos_selected_item.html +++ b/erpnext/public/js/pos/pos_selected_item.html @@ -13,7 +13,7 @@
    {{ __("Price") }}:
    - +
    {{ __("Amount") }}:
    diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 4ae4738841..81570138a0 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -14,6 +15,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -44,6 +46,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -73,6 +76,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -103,6 +107,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -134,6 +139,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -165,6 +171,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -194,6 +201,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -222,6 +230,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -254,6 +263,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -286,6 +296,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -315,6 +326,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -345,6 +357,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -374,6 +387,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -403,6 +417,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -432,6 +447,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -460,6 +476,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -490,6 +507,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -520,6 +538,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -548,6 +567,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -576,6 +596,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -604,6 +625,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -633,6 +655,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -662,6 +685,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -693,6 +717,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -723,6 +748,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -753,6 +779,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -784,6 +811,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -815,6 +843,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -846,6 +875,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -877,6 +907,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -906,6 +937,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -937,6 +969,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -968,6 +1001,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -998,6 +1032,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -1028,6 +1063,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1057,8 +1093,39 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "customer_pos_id", + "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": "Customer POS id", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-user", @@ -1066,12 +1133,11 @@ "image_field": "image", "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-20 13:21:53.659049", + "modified": "2017-06-05 13:58:19.258783", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From f981eee2218d9f63b13b886d3238b32257053aa4 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 7 Jun 2017 07:35:00 +0100 Subject: [PATCH 13/14] fixes issue #9013: Salary calculation Error. (#9044) * fixes issue #9013: Salary calculation Error. * adds test case for scenario as in #9013 --- erpnext/hr/doctype/salary_slip/salary_slip.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index e18fc27e67..1cee02203d 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -91,7 +91,7 @@ class SalarySlip(TransactionBase): frappe.throw(_("Name error: {0}".format(err))) except SyntaxError as err: frappe.throw(_("Syntax error in formula or condition: {0}".format(err))) - except Exception, e: + except Exception as e: frappe.throw(_("Error in formula or condition: {0}".format(e))) raise @@ -330,11 +330,18 @@ class SalarySlip(TransactionBase): frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) for d in self.get(component_type): - if self.salary_structure and ((cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet) or\ - getdate(self.start_date) < joining_date or getdate(self.end_date) > relieving_date): + if (self.salary_structure and + cint(d.depends_on_lwp) and + (not + self.salary_slip_based_on_timesheet or + getdate(self.start_date) < joining_date or + getdate(self.end_date) > relieving_date + )): - d.amount = rounded((flt(d.default_amount) * flt(self.payment_days) - / cint(self.total_working_days)), self.precision("amount", component_type)) + d.amount = rounded( + (flt(d.default_amount) * flt(self.payment_days) + / cint(self.total_working_days)), self.precision("amount", component_type) + ) elif not self.payment_days and not self.salary_slip_based_on_timesheet: d.amount = 0 elif not d.amount: From d0e530c63d69e90939d0d5ac25f87cae2a11d291 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 7 Jun 2017 12:42:11 +0600 Subject: [PATCH 14/14] bumped to version 8.0.45 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f93c9679b7..7ce777213f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '8.0.44' +__version__ = '8.0.45' def get_default_company(user=None):