From 66069df02064ee19af9b8dc429b56f1a86d01956 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 31 Jul 2020 15:54:05 +0530 Subject: [PATCH 001/283] feat: Return tracking in PR/DN --- .../purchase_order/purchase_order.json | 4 +- .../controllers/sales_and_purchase_return.py | 1 + erpnext/controllers/status_updater.py | 21 ++++++--- .../purchase_receipt/purchase_receipt.json | 14 +++++- .../purchase_receipt/purchase_receipt.py | 43 ++++++++++++------- .../purchase_receipt/purchase_receipt_list.js | 2 + .../purchase_receipt_item.json | 25 +++++++++-- 7 files changed, 81 insertions(+), 29 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 502dbba571..858dec0b1e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1084,7 +1084,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:09:33.800633", + "modified": "2020-07-31 14:39:44.599294", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1135,5 +1135,5 @@ "sort_field": "modified", "sort_order": "DESC", "timeline_field": "supplier", - "title_field": "title" + "title_field": "supplier" } \ No newline at end of file diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 3f127a201e..1085486f94 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -207,6 +207,7 @@ def make_return_doc(doctype, source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc company = frappe.db.get_value("Delivery Note", source_name, "company") default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return") + def set_missing_values(source, target): doc = frappe.get_doc(target) doc.is_return = 1 diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 0dc9878afd..bf69130e4a 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -65,6 +65,7 @@ status_map = { "Purchase Receipt": [ ["Draft", None], ["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"], + ["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"], ["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], @@ -232,7 +233,7 @@ class StatusUpdater(Document): self._update_children(args, update_modified) - if "percent_join_field" in args: + if "percent_join_field" in args or "percent_join_field_parent" in args: self._update_percent_field_in_targets(args, update_modified) def _update_children(self, args, update_modified): @@ -272,13 +273,19 @@ class StatusUpdater(Document): def _update_percent_field_in_targets(self, args, update_modified=True): """Update percent field in parent transaction""" - distinct_transactions = set([d.get(args['percent_join_field']) - for d in self.get_all_children(args['source_dt'])]) + if args.get('percent_join_field_parent'): + # if reference to target doc where % is to be updated, is + # in source doc's parent form, consider percent_join_field_parent + args['name'] = self.get(args['percent_join_field_parent']) + self._update_percent_field(args, update_modified) + else: + distinct_transactions = set([d.get(args['percent_join_field']) + for d in self.get_all_children(args['source_dt'])]) - for name in distinct_transactions: - if name: - args['name'] = name - self._update_percent_field(args, update_modified) + for name in distinct_transactions: + if name: + args['name'] = name + self._update_percent_field(args, update_modified) def _update_percent_field(self, args, update_modified=True): """Update percent field in parent transaction""" diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 92e33ca64e..fac8909e53 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1,7 +1,6 @@ { "actions": [], "allow_import": 1, - "allow_workflow": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", "doctype": "DocType", @@ -111,6 +110,7 @@ "range", "column_break4", "per_billed", + "per_returned", "is_internal_supplier", "inter_company_reference", "subscription_detail", @@ -1104,13 +1104,23 @@ "fieldtype": "Small Text", "label": "Billing Address", "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_returned", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Returned", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:19:12.148115", + "modified": "2020-07-31 15:16:26.811384", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d0ba001d7e..dafaae28c5 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -55,20 +55,33 @@ class PurchaseReceipt(BuyingController): 'percent_join_field': 'material_request' }] if cint(self.is_return): - self.status_updater.append({ - 'source_dt': 'Purchase Receipt Item', - 'target_dt': 'Purchase Order Item', - 'join_field': 'purchase_order_item', - 'target_field': 'returned_qty', - 'source_field': '-1 * qty', - 'second_source_dt': 'Purchase Invoice Item', - 'second_source_field': '-1 * qty', - 'second_join_field': 'po_detail', - 'extra_cond': """ and exists (select name from `tabPurchase Receipt` - where name=`tabPurchase Receipt Item`.parent and is_return=1)""", - 'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice` - where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)""" - }) + self.status_updater.extend([ + { + 'source_dt': 'Purchase Receipt Item', + 'target_dt': 'Purchase Order Item', + 'join_field': 'purchase_order_item', + 'target_field': 'returned_qty', + 'source_field': '-1 * qty', + 'second_source_dt': 'Purchase Invoice Item', + 'second_source_field': '-1 * qty', + 'second_join_field': 'po_detail', + 'extra_cond': """ and exists (select name from `tabPurchase Receipt` + where name=`tabPurchase Receipt Item`.parent and is_return=1)""", + 'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice` + where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)""" + }, + { + 'source_dt': 'Purchase Receipt Item', + 'target_dt': 'Purchase Receipt Item', + 'join_field': 'purchase_receipt_item', + 'target_field': 'returned_qty', + 'target_parent_dt': 'Purchase Receipt', + 'target_parent_field': 'per_returned', + 'target_ref_field': 'stock_qty', + 'source_field': '-1 * stock_qty', + 'percent_join_field_parent': 'return_against' + } + ]) def validate(self): self.validate_posting_time() @@ -470,7 +483,7 @@ class PurchaseReceipt(BuyingController): frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate)) def update_status(self, status): - self.set_status(update=True, status = status) + self.set_status(update=True, status=status) self.notify_update() clear_doctype_notifications(self) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index e81f323a46..7bf5a1cb94 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -6,6 +6,8 @@ frappe.listview_settings['Purchase Receipt'] = { return [__("Return"), "darkgrey", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; + } else if (flt(doc.per_returned, 2) == 100) { + return [__("Return Issued"), "grey", "per_returned,=,100"]; } else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) { return [__("To Bill"), "orange", "per_billed,<,100"]; } else if (flt(doc.grand_total) === 0 || flt(doc.per_billed, 2) == 100) { diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index c1e1f901ba..a9f31aa74b 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -28,9 +28,12 @@ "uom", "stock_uom", "conversion_factor", - "stock_qty", "retain_sample", "sample_quantity", + "tracking_section", + "stock_qty", + "col_break_tracking_section", + "returned_qty", "rate_and_amount", "price_list_rate", "discount_percentage", @@ -526,7 +529,7 @@ { "fieldname": "stock_qty", "fieldtype": "Float", - "label": "Accepted Qty as per Stock UOM", + "label": "Accepted Qty in Stock UOM", "oldfieldname": "stock_qty", "oldfieldtype": "Currency", "print_hide": 1, @@ -834,12 +837,28 @@ "collapsible": 1, "fieldname": "image_column", "fieldtype": "Column Break" + }, + { + "fieldname": "tracking_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "col_break_tracking_section", + "fieldtype": "Column Break" + }, + { + "depends_on": "returned_qty", + "fieldname": "returned_qty", + "fieldtype": "Float", + "label": "Returned Qty in Stock UOM", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-28 19:01:21.154963", + "modified": "2020-07-30 21:02:17.912628", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From ba37fe796cbcd825ff44dba42b1acfb883e546c6 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 31 Jul 2020 20:01:06 +0530 Subject: [PATCH 002/283] chore: Delivery Note Return status --- erpnext/controllers/status_updater.py | 1 + .../doctype/delivery_note/delivery_note.json | 13 +++++++++- .../doctype/delivery_note/delivery_note.py | 16 ++++++++++-- .../delivery_note/delivery_note_list.js | 2 ++ .../delivery_note_item.json | 25 +++++++++++++++++-- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index bf69130e4a..e776dc96ac 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -58,6 +58,7 @@ status_map = { "Delivery Note": [ ["Draft", None], ["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"], + ["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"], ["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 66efcf8cd8..2b47d8c248 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -132,6 +132,7 @@ "per_installed", "installation_status", "column_break_89", + "per_returned", "excise_page", "instructions", "subscription_section", @@ -1249,13 +1250,23 @@ "fieldtype": "Link", "label": "Inter Company Reference", "options": "Purchase Receipt" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "per_returned", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Returned", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:13:55.580420", + "modified": "2020-07-31 19:56:19.800171", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d04cf785ab..895295bbfe 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -55,7 +55,7 @@ class DeliveryNote(SellingController): 'no_allowance': 1 }] if cint(self.is_return): - self.status_updater.append({ + self.status_updater.extend([{ 'source_dt': 'Delivery Note Item', 'target_dt': 'Sales Order Item', 'join_field': 'so_detail', @@ -69,7 +69,19 @@ class DeliveryNote(SellingController): where name=`tabDelivery Note Item`.parent and is_return=1)""", 'second_source_extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)""" - }) + }, + { + 'source_dt': 'Delivery Note Item', + 'target_dt': 'Delivery Note Item', + 'join_field': 'dn_detail', + 'target_field': 'returned_qty', + 'target_parent_dt': 'Delivery Note', + 'target_parent_field': 'per_returned', + 'target_ref_field': 'stock_qty', + 'source_field': '-1 * stock_qty', + 'percent_join_field_parent': 'return_against' + } + ]) def before_print(self): def toggle_print_hide(meta, fieldname): diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index 0ae7c37b3f..a0579446d0 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -6,6 +6,8 @@ frappe.listview_settings['Delivery Note'] = { return [__("Return"), "darkgrey", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; + } else if (flt(doc.per_returned, 2) == 100) { + return [__("Return Issued"), "grey", "per_returned,=,100"]; } else if (flt(doc.per_billed, 2) < 100) { return [__("To Bill"), "orange", "per_billed,<,100"]; } else if (flt(doc.per_billed, 2) == 100) { diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 3d57f47601..eb99fed986 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-04-22 13:15:44", "doctype": "DocType", @@ -24,8 +25,11 @@ "col_break2", "uom", "conversion_factor", + "stock_qty_sec_break", "stock_qty", + "stock_qty_col_break", "section_break_17", + "returned_qty", "price_list_rate", "base_price_list_rate", "discount_and_margin", @@ -211,7 +215,7 @@ { "fieldname": "stock_qty", "fieldtype": "Float", - "label": "Qty as per Stock UOM", + "label": "Qty in Stock UOM", "no_copy": 1, "print_hide": 1, "read_only": 1 @@ -715,12 +719,29 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "stock_qty_sec_break", + "fieldtype": "Section Break" + }, + { + "fieldname": "stock_qty_col_break", + "fieldtype": "Column Break" + }, + { + "depends_on": "returned_qty", + "fieldname": "returned_qty", + "fieldtype": "Float", + "label": "Returned Qty in Stock UOM", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-07-20 12:25:06.177894", + "modified": "2020-07-31 19:43:46.152260", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", From d36df9275040a5b891efbdfe44a554b4ca1e15ab Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 31 Jul 2020 20:22:55 +0530 Subject: [PATCH 003/283] fix: Codacy and repositioned Returned Qty in DN Item --- erpnext/stock/doctype/delivery_note/delivery_note_list.js | 4 ++-- .../stock/doctype/delivery_note_item/delivery_note_item.json | 4 ++-- .../stock/doctype/purchase_receipt/purchase_receipt_list.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index a0579446d0..4a6500cfd8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -6,11 +6,11 @@ frappe.listview_settings['Delivery Note'] = { return [__("Return"), "darkgrey", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; - } else if (flt(doc.per_returned, 2) == 100) { + } else if (flt(doc.per_returned, 2) === 100) { return [__("Return Issued"), "grey", "per_returned,=,100"]; } else if (flt(doc.per_billed, 2) < 100) { return [__("To Bill"), "orange", "per_billed,<,100"]; - } else if (flt(doc.per_billed, 2) == 100) { + } else if (flt(doc.per_billed, 2) === 100) { return [__("Completed"), "green", "per_billed,=,100"]; } }, diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index eb99fed986..7b471874af 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -28,8 +28,8 @@ "stock_qty_sec_break", "stock_qty", "stock_qty_col_break", - "section_break_17", "returned_qty", + "section_break_17", "price_list_rate", "base_price_list_rate", "discount_and_margin", @@ -741,7 +741,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-07-31 19:43:46.152260", + "modified": "2020-07-31 20:12:43.054342", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index 7bf5a1cb94..c9501a409a 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -6,11 +6,11 @@ frappe.listview_settings['Purchase Receipt'] = { return [__("Return"), "darkgrey", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; - } else if (flt(doc.per_returned, 2) == 100) { + } else if (flt(doc.per_returned, 2) === 100) { return [__("Return Issued"), "grey", "per_returned,=,100"]; } else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) { return [__("To Bill"), "orange", "per_billed,<,100"]; - } else if (flt(doc.grand_total) === 0 || flt(doc.per_billed, 2) == 100) { + } else if (flt(doc.grand_total) === 0 || flt(doc.per_billed, 2) === 100) { return [__("Completed"), "green", "per_billed,=,100"]; } } From 2744765757cfeb9a2f9e9671d91e945ad4b6bcaf Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 7 Sep 2020 12:31:19 +0530 Subject: [PATCH 004/283] feat(UAE VAT Format): add fields for emirates in address and sales invoice --- erpnext/regional/united_arab_emirates/setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 250659e54d..a2938bb300 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -41,6 +41,8 @@ def make_custom_fields(): dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), + dict(fieldname='emirate', label='Emirate', insert_after='customer_address', + fetch_from='customer_address.emirates'), ] invoice_item_fields = [ @@ -76,6 +78,10 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Data', insert_after='supplier_name'), ], + 'Address': [ + dict(fieldname='emirates', label='Emirates', + fieldtype='Data', insert_after='state'), + ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, 'Purchase Receipt': purchase_invoice_fields + invoice_fields, From e35fd5e3051b1ce84417c068d8e5bedf738c8620 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 8 Sep 2020 16:24:10 +0530 Subject: [PATCH 005/283] chore: Tests for Purchase Receipt --- .../doctype/delivery_note/delivery_note.json | 4 +-- .../purchase_receipt/purchase_receipt.json | 4 +-- .../purchase_receipt/test_purchase_receipt.py | 31 +++++++++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index b7f080f983..32fe760e05 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1098,7 +1098,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", + "options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed", "print_hide": 1, "print_width": "150px", "read_only": 1, @@ -1266,7 +1266,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-08-03 23:18:47.739997", + "modified": "2020-09-08 11:22:09.056684", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 723908854d..bbfaeabaf7 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -895,7 +895,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", + "options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed", "print_hide": 1, "print_width": "150px", "read_only": 1, @@ -1120,7 +1120,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-08-03 23:20:26.381024", + "modified": "2020-09-08 11:21:25.465966", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 67161aa6dd..bdc6c3a82a 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -255,11 +255,13 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"), pr.get("items")[0].rejected_warehouse) - def test_purchase_return(self): + def test_purchase_return_partial(self): pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") - return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-2) + return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-2, do_not_submit=1) + return_pr.items[0].purchase_receipt_item = pr.items[0].name + return_pr.submit() # check sle outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", @@ -283,6 +285,31 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.credit) + # hack because new_doc isn't considering is_return portion of status_updater + returned = frappe.get_doc("Purchase Receipt", return_pr.name) + returned.update_prevdoc_status() + pr.load_from_db() + + # Check if Original PR updated + self.assertEqual(pr.items[0].returned_qty, 2) + self.assertEqual(pr.per_returned, 40) + + def test_purchase_return_full(self): + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") + + return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-5, do_not_submit=1) + return_pr.items[0].purchase_receipt_item = pr.items[0].name + return_pr.submit() + + # hack because new_doc isn't considering is_return portion of status_updater + returned = frappe.get_doc("Purchase Receipt", return_pr.name) + returned.update_prevdoc_status() + pr.load_from_db() + + # Check if Original PR updated + self.assertEqual(pr.items[0].returned_qty, 5) + self.assertEqual(pr.per_returned, 100) + self.assertEqual(pr.status, 'Return Issued') def test_purchase_return_for_rejected_qty(self): from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse From 315bf3051c5f4e1221b89129d264e0d5af522a05 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 8 Sep 2020 19:27:36 +0530 Subject: [PATCH 006/283] chore: Tests for Delivery Note --- .../delivery_note/test_delivery_note.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 4b04a0a8c3..ecd2d693a0 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -206,7 +206,7 @@ class TestDeliveryNote(unittest.TestCase): for field, value in field_values.items(): self.assertEqual(cstr(serial_no.get(field)), value) - def test_sales_return_for_non_bundled_items(self): + def test_sales_return_for_non_bundled_items_partial(self): company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100) @@ -225,7 +225,10 @@ class TestDeliveryNote(unittest.TestCase): # return entry dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500, - company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1") + company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", do_not_submit=1) + dn1.items[0].dn_detail = dn.items[0].name + dn1.submit() actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1") @@ -243,6 +246,42 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(gle_warehouse_amount, stock_value_difference) + # hack because new_doc isn't considering is_return portion of status_updater + returned = frappe.get_doc("Delivery Note", dn1.name) + returned.update_prevdoc_status() + dn.load_from_db() + + # Check if Original DN updated + self.assertEqual(dn.items[0].returned_qty, 2) + self.assertEqual(dn.per_returned, 40) + + def test_sales_return_for_non_bundled_items_full(self): + company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') + + make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100) + + actual_qty_0 = get_qty_after_transaction(warehouse="Stores - TCP1") + + dn = create_delivery_note(qty=5, rate=500, warehouse="Stores - TCP1", company=company, + expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1") + + #return entry + dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-5, rate=500, + company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", do_not_submit=1) + dn1.items[0].dn_detail = dn.items[0].name + dn1.submit() + + # hack because new_doc isn't considering is_return portion of status_updater + returned = frappe.get_doc("Delivery Note", dn1.name) + returned.update_prevdoc_status() + dn.load_from_db() + + # Check if Original DN updated + self.assertEqual(dn.items[0].returned_qty, 5) + self.assertEqual(dn.per_returned, 100) + self.assertEqual(dn.status, 'Return Issued') + def test_return_single_item_from_bundled_items(self): company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') From 8484d1cd957421c9b038e521484bbc0f3df8a69c Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 8 Sep 2020 20:32:25 +0530 Subject: [PATCH 007/283] fix: Patch and test codacy --- .../patches/v7_0/po_status_issue_for_pr_return.py | 12 ++++++++---- .../doctype/delivery_note/test_delivery_note.py | 2 -- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/patches/v7_0/po_status_issue_for_pr_return.py b/erpnext/patches/v7_0/po_status_issue_for_pr_return.py index 6e92ffb8a0..910814fd22 100644 --- a/erpnext/patches/v7_0/po_status_issue_for_pr_return.py +++ b/erpnext/patches/v7_0/po_status_issue_for_pr_return.py @@ -7,19 +7,23 @@ import frappe def execute(): parent_list = [] count = 0 - for data in frappe.db.sql(""" - select + + frappe.reload_doc('stock', 'doctype', 'purchase_receipt') + frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item') + + for data in frappe.db.sql(""" + select `tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name, `tabPurchase Receipt Item`.item_code, `tabPurchase Receipt Item`.idx, `tabPurchase Receipt Item`.parent - from + from `tabPurchase Receipt Item`, `tabPurchase Receipt` where `tabPurchase Receipt Item`.parent = `tabPurchase Receipt`.name and `tabPurchase Receipt Item`.purchase_order_item is null and `tabPurchase Receipt Item`.purchase_order is not null and `tabPurchase Receipt`.is_return = 1""", as_dict=1): - name = frappe.db.get_value('Purchase Order Item', + name = frappe.db.get_value('Purchase Order Item', {'item_code': data.item_code, 'parent': data.purchase_order, 'idx': data.idx}, 'name') if name: diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index ecd2d693a0..339ea57dd9 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -260,8 +260,6 @@ class TestDeliveryNote(unittest.TestCase): make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100) - actual_qty_0 = get_qty_after_transaction(warehouse="Stores - TCP1") - dn = create_delivery_note(qty=5, rate=500, warehouse="Stores - TCP1", company=company, expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1") From efb211af6dc1b47e44118086503209c8097c10bf Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 9 Sep 2020 16:24:11 +0530 Subject: [PATCH 008/283] chore: Fix Received/Delivered Items to Billed Logic --- .../delivered_items_to_be_billed.py | 18 ++++++++++++----- erpnext/accounts/report/non_billed_report.py | 20 +++++++++++++------ .../received_items_to_be_billed.py | 18 ++++++++++++----- .../purchase_receipt_item.json | 3 ++- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py index 3ffb3ac1df..2aea3f6423 100644 --- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py +++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py @@ -14,11 +14,19 @@ def execute(filters=None): def get_column(): return [ - _("Delivery Note") + ":Link/Delivery Note:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120", - _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100", - _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", + _("Delivery Note") + ":Link/Delivery Note:160", + _("Date") + ":Date:100", + _("Customer") + ":Link/Customer:120", + _("Customer Name") + "::120", + _("Item Code") + ":Link/Item:120", + _("Amount") + ":Currency:100", + _("Billed Amount") + ":Currency:100", + _("Returned Amount") + ":Currency:120", + _("Pending Amount") + ":Currency:100", + _("Item Name") + "::120", + _("Description") + "::120", + _("Project") + ":Link/Project:120", + _("Company") + ":Link/Company:120", ] def get_args(): diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py index a9e25bc25b..2e18ce11dd 100644 --- a/erpnext/accounts/report/non_billed_report.py +++ b/erpnext/accounts/report/non_billed_report.py @@ -17,18 +17,26 @@ def get_ordered_to_be_billed_data(args): return frappe.db.sql(""" Select - `{parent_tab}`.name, `{parent_tab}`.status, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name, - {project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount, + `{parent_tab}`.name, `{parent_tab}`.{date_field}, + `{parent_tab}`.{party}, `{parent_tab}`.{party}_name, + `{child_tab}`.item_code, + `{child_tab}`.base_amount, (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)), - (`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))), - `{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company + (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)), + (`{child_tab}`.base_amount - + (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) - + (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))), + `{child_tab}`.item_name, `{child_tab}`.description, + {project_field}, `{parent_tab}`.company from `{parent_tab}`, `{child_tab}` where `{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1 and `{parent_tab}`.status not in ('Closed', 'Completed') - and `{child_tab}`.amount > 0 and round(`{child_tab}`.billed_amt * - ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) < `{child_tab}`.base_amount + and `{child_tab}`.amount > 0 + and (`{child_tab}`.base_amount - + round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) - + (`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0 order by `{parent_tab}`.{order} {order_by} """.format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party, diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py index 5e8d7730b7..c7d4384a73 100644 --- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py +++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py @@ -14,11 +14,19 @@ def execute(filters=None): def get_column(): return [ - _("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Supplier") + ":Link/Supplier:120", _("Supplier 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", + _("Purchase Receipt") + ":Link/Purchase Receipt:160", + _("Date") + ":Date:100", + _("Supplier") + ":Link/Supplier:120", + _("Supplier Name") + "::120", + _("Item Code") + ":Link/Item:120", + _("Amount") + ":Currency:100", + _("Billed Amount") + ":Currency:100", + _("Returned Amount") + ":Currency:120", + _("Pending Amount") + ":Currency:120", + _("Item Name") + "::120", + _("Description") + "::120", + _("Project") + ":Link/Project:120", + _("Company") + ":Link/Company:120", ] def get_args(): diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index a9f31aa74b..20ae56feeb 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -851,6 +851,7 @@ "fieldname": "returned_qty", "fieldtype": "Float", "label": "Returned Qty in Stock UOM", + "no_copy": 1, "print_hide": 1, "read_only": 1 } @@ -858,7 +859,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-07-30 21:02:17.912628", + "modified": "2020-09-09 13:39:46.452817", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From dc5f2aa8b8dae91a59b43d8403023f0085ca1aa6 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 9 Sep 2020 17:56:20 +0530 Subject: [PATCH 009/283] chore: Patch to set returned qty in PR and DN --- erpnext/patches.txt | 1 + .../v13_0/update_returned_qty_in_pr_dn.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6c58f2f452..a0a49b4e2a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -725,3 +725,4 @@ erpnext.patches.v12_0.rename_lost_reason_detail erpnext.patches.v13_0.drop_razorpay_payload_column erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports +erpnext.patches.v13_0.update_returned_qty_in_pr_dn \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py new file mode 100644 index 0000000000..a13640e1b0 --- /dev/null +++ b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py @@ -0,0 +1,20 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc('stock', 'doctype', 'purchase_receipt') + frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item') + frappe.reload_doc('stock', 'doctype', 'delivery_note') + frappe.reload_doc('stock', 'doctype', 'delivery_note_item') + + def update_from_return_docs(doctype): + for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1}): + # Update original receipt/delivery document from return + return_doc = frappe.get_cached_doc(doctype, return_doc.name) + return_doc.update_prevdoc_status() + + for doctype in ('Purchase Receipt', 'Delivery Note'): + update_from_return_docs(doctype) \ No newline at end of file From 83a03689a1145505b107d89919360e1ca2ec19f0 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 9 Sep 2020 20:51:01 +0530 Subject: [PATCH 010/283] fix: Use independent item for DN Test --- .../stock/doctype/delivery_note/test_delivery_note.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 339ea57dd9..5d180eaab0 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -256,15 +256,19 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(dn.per_returned, 40) def test_sales_return_for_non_bundled_items_full(self): + from erpnext.stock.doctype.item.test_item import make_item + company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') - make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100) + make_item("Box", {'is_stock_item': 1}) - dn = create_delivery_note(qty=5, rate=500, warehouse="Stores - TCP1", company=company, + make_stock_entry(item_code="Box", target="Stores - TCP1", qty=10, basic_rate=100) + + dn = create_delivery_note(item_code="Box", qty=5, rate=500, warehouse="Stores - TCP1", company=company, expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1") #return entry - dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-5, rate=500, + dn1 = create_delivery_note(item_code="Box", is_return=1, return_against=dn.name, qty=-5, rate=500, company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1", do_not_submit=1) dn1.items[0].dn_detail = dn.items[0].name From 7b7a8e1309ba0c262da7de07424858f5de9751d3 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Wed, 9 Sep 2020 20:54:30 +0530 Subject: [PATCH 011/283] feat: RCM for UAE VAT --- erpnext/hooks.py | 8 +- .../regional/united_arab_emirates/utils.py | 144 +++++++++++++++++- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 463ad6c94b..72be2dcc75 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -246,7 +246,10 @@ doc_events = { "on_trash": "erpnext.regional.check_deletion_permission" }, "Purchase Invoice": { - "validate": "erpnext.regional.india.utils.update_grand_total_for_rcm" + "validate": [ + "erpnext.regional.india.utils.update_grand_total_for_rcm", + "erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm", + ] }, "Payment Entry": { "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"], @@ -379,7 +382,8 @@ regional_overrides = { 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries' }, 'United Arab Emirates': { - 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data' + 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data', + 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries', }, 'Saudi Arabia': { 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data' diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a0425f6b1c..05a99d190a 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 frappe import _ +import erpnext +from frappe.utils import flt, round_based_on_smallest_currency_fraction, money_in_words from erpnext.controllers.taxes_and_totals import get_itemised_tax from six import iteritems @@ -26,4 +28,142 @@ def update_itemised_tax_data(doc): row.tax_rate = flt(tax_rate, row.precision("tax_rate")) row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount")) - row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount")) \ No newline at end of file + row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount")) + +def get_account_currency(account): + """Helper function to get account currency""" + if not account: + return + def generator(): + account_currency, company = frappe.get_cached_value("Account", account, ["account_currency", "company"]) + if not account_currency: + account_currency = frappe.get_cached_value('Company', company, "default_currency") + + return account_currency + + return frappe.local_cache("account_currency", account, generator) + +def get_tax_accounts(company): + """Get the list of tax accounts for a specific company + + Args: + company (String): Current Company set as default + + Returns: + tax_accounts: List of Tax Accounts for the company + """ + tax_accounts_dict = frappe._dict() + tax_accounts_list = frappe.get_all("Account", + filters={"account_type": "Tax", "company": company}, + fields=["name"]) + + if not tax_accounts_list and not frappe.flags.in_test: + frappe.throw(_("Please create at least one Account of type Tax")) + for d in tax_accounts_list: + for key, name in d.items(): + tax_accounts_dict[name] = name + + return tax_accounts_dict + +def update_grand_total_for_rcm(doc, method): + """If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form + + Args: + doc (Document): The document for the current Purchase Invoice + """ + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'United Arab Emirates': + return + + if not doc.total_taxes_and_charges: + return + + if doc.reverse_charge == 'Y': + tax_accounts = get_tax_accounts(doc.company) + + base_vat_tax = 0 + vat_tax = 0 + + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts: + base_vat_tax += tax.base_tax_amount_after_discount_amount + vat_tax += tax.tax_amount_after_discount_amount + + doc.taxes_and_charges_added -= vat_tax + doc.total_taxes_and_charges -= vat_tax + doc.base_taxes_and_charges_added -= base_vat_tax + doc.base_total_taxes_and_charges -= base_vat_tax + + update_totals(vat_tax, base_vat_tax, doc) + +def update_totals(vat_tax, base_vat_tax, doc): + """Update the grand total values in the form + + Args: + vat_tax (float): Vat Tax to be subtracted + base_vat_tax (float): Base Vat Tax to be subtracted + doc (Document): The document for the current Purchase Invoice + """ + + doc.base_grand_total -= base_vat_tax + doc.grand_total -= vat_tax + + if doc.meta.get_field("rounded_total"): + + if doc.is_rounded_total_disabled(): + doc.outstanding_amount = doc.grand_total + + else: + doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total, + doc.currency, doc.precision("rounded_total")) + doc.rounding_adjustment = flt(doc.rounded_total - doc.grand_total, + doc.precision("rounding_adjustment")) + doc.outstanding_amount = doc.rounded_total or doc.grand_total + + doc.in_words = money_in_words(doc.grand_total, doc.currency) + doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company)) + doc.set_payment_schedule() + +def make_regional_gl_entries(gl_entries, doc): + """This method is hooked to the make_regional_gl_entries in Purchase Invoice. + It appends the region specific general ledger entries to the list of GL Entries. + + Args: + gl_entries (List): List of GL entries to be made + doc (Document): The document for the current Purchase Invoice + + Returns: + List: Updates list of GL Entries + """ + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'United Arab Emirates': + return gl_entries + + if doc.reverse_charge == 'Y': + tax_accounts = get_tax_accounts(doc.company) + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts: + account_currency = get_account_currency(tax.account_head) + + gl_entries.append(doc.get_gl_dict( + { + "account": tax.account_head, + "cost_center": tax.cost_center, + "posting_date": doc.posting_date, + "against": doc.supplier, + dr_or_cr: tax.base_tax_amount_after_discount_amount, + dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=tax) + ) + return gl_entries \ No newline at end of file From 91f1e266726c20abd2afc797c0c12f0e8793a3c5 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 10 Sep 2020 13:55:14 +0530 Subject: [PATCH 012/283] feat: Add rated supplies rows in UAE VAT report --- erpnext/regional/report/uae_vat/__init__.py | 0 erpnext/regional/report/uae_vat/uae_vat.js | 31 ++++++ erpnext/regional/report/uae_vat/uae_vat.json | 22 ++++ erpnext/regional/report/uae_vat/uae_vat.py | 108 +++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 erpnext/regional/report/uae_vat/__init__.py create mode 100644 erpnext/regional/report/uae_vat/uae_vat.js create mode 100644 erpnext/regional/report/uae_vat/uae_vat.json create mode 100644 erpnext/regional/report/uae_vat/uae_vat.py diff --git a/erpnext/regional/report/uae_vat/__init__.py b/erpnext/regional/report/uae_vat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/uae_vat/uae_vat.js b/erpnext/regional/report/uae_vat/uae_vat.js new file mode 100644 index 0000000000..45df167823 --- /dev/null +++ b/erpnext/regional/report/uae_vat/uae_vat.js @@ -0,0 +1,31 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["UAE VAT"] = { + "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", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3), + "width": "80" + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + ] +}; diff --git a/erpnext/regional/report/uae_vat/uae_vat.json b/erpnext/regional/report/uae_vat/uae_vat.json new file mode 100644 index 0000000000..8807405a98 --- /dev/null +++ b/erpnext/regional/report/uae_vat/uae_vat.json @@ -0,0 +1,22 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2020-09-10 08:51:02.298482", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2020-09-10 08:51:02.298482", + "modified_by": "Administrator", + "module": "Regional", + "name": "UAE VAT", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "UAE VAT", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py new file mode 100644 index 0000000000..6e9565b1e5 --- /dev/null +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -0,0 +1,108 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe + +def execute(filters=None): + columns = get_columns() + data = get_data(filters) + + return columns, data + +def get_columns(): + return [ + { + "fieldname": "no", + "label": "No", + "fieldtype": "Data", + "width": 50 + }, + { + "fieldname": "legend", + "label": "Legend", + "fieldtype": "Data", + "width": 300 + }, + { + "fieldname": "amount", + "label": "Amount (AED)", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "vat_amount", + "label": "VAT Amount (AED)", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "adjustment", + "label": "Adjustment (AED)", + "fieldtype": "Currency", + "width": 100 + } + ] + +def get_data(filters = None): + data = [] + total_emiratewise = get_total_emiratewise(filters) + emirates = get_emirates() + amounts_by_emirate = {} + for d in total_emiratewise: + emirate, amount, vat= d + amounts_by_emirate[emirate] = { + "legend": emirate, + "amount": amount, + "vat_amount": vat + } + for d, emirate in enumerate(emirates, 97): + if emirate in amounts_by_emirate: + amounts_by_emirate[emirate]["no"] = f'1{chr(d)}' + amounts_by_emirate[emirate]["legend"] = f'Standard rated supplies in {emirate}' + data.append(amounts_by_emirate[emirate]) + else: + data.append( + { + "no": f'1{chr(d)}', + "legend": f'Standard rated supplies in {emirate}', + "amount": 0, + "vat_amount": 0 + } + ) + return data + + +def get_total_emiratewise(filters): + conditions = get_conditions(filters) + print(f""" + select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` + where docstatus = 1 {conditions} + group by `tabSales Invoice`.emirate; + """) + return frappe.db.sql(f""" + select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` + where docstatus = 1 {conditions} + group by `tabSales Invoice`.emirate; + """, filters) + +def get_emirates(): + return [ + 'Abu Dhabi', + 'Dubai', + 'Sharjah', + 'Ajman', + 'Umm Al Quwain', + 'Ras Al Khaimah', + 'Fujairah' + ] + +def get_conditions(filters): + conditions = "" + + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if filters.get(opts[0]): + conditions += opts[1] + return conditions \ No newline at end of file From 031d77be2e58a033786a81354f08607ea07e4910 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 10 Sep 2020 17:40:01 +0530 Subject: [PATCH 013/283] feat: add reverse charge to uae vat report --- erpnext/regional/report/uae_vat/uae_vat.py | 53 +++++++++++++++---- .../regional/united_arab_emirates/setup.py | 8 +-- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py index 6e9565b1e5..50d1ddd351 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.py +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from erpnext.regional.united_arab_emirates.utils import get_tax_accounts def execute(filters=None): columns = get_columns() @@ -70,19 +71,21 @@ def get_data(filters = None): "vat_amount": 0 } ) + data.append( + { + "no": '3', + "legend": f'Supplies subject to the reverse charge provision', + "amount": get_reverse_charge_total(filters), + "vat_amount": get_reverse_charge_tax(filters) + } + ) return data def get_total_emiratewise(filters): - conditions = get_conditions(filters) - print(f""" - select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` - where docstatus = 1 {conditions} - group by `tabSales Invoice`.emirate; - """) return frappe.db.sql(f""" select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` - where docstatus = 1 {conditions} + where docstatus = 1 {get_conditions(filters)} group by `tabSales Invoice`.emirate; """, filters) @@ -99,10 +102,40 @@ def get_emirates(): def get_conditions(filters): conditions = "" + for opts in (("company", f' and company="{filters.get("company")}"'), + ("from_date", f' and posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + if filters.get(opts[0]): + conditions += opts[1] + return conditions - for opts in (("company", " and company=%(company)s"), - ("from_date", " and posting_date>=%(from_date)s"), - ("to_date", " and posting_date<=%(to_date)s")): +def get_reverse_charge_tax(filters): + return frappe.db.sql(f""" + select sum(debit) from + `tabPurchase Invoice` inner join `tabGL Entry` + on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name + where + `tabPurchase Invoice`.reverse_charge = "Y" + and `tabPurchase Invoice`.docstatus = 1 + and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} + and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); + """)[0][0] + + +def get_reverse_charge_total(filters): + return frappe.db.sql(f""" + select sum(total) from + `tabPurchase Invoice` + where + reverse_charge = "Y" + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + +def get_conditions_join(filters): + conditions = "" + for opts in (("company", f' and `tabPurchase Invoice`.company="{filters.get("company")}"'), + ("from_date", f' and `tabPurchase Invoice`.posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and `tabPurchase Invoice`.posting_date<="{filters.get("to_date")}"')): if filters.get(opts[0]): conditions += opts[1] return conditions \ No newline at end of file diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index a2938bb300..d38d647277 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -20,7 +20,7 @@ def make_custom_fields(): insert_after='group_same_items', print_hide=1, collapsible=1), dict(fieldname='permit_no', label='Permit Number', fieldtype='Data', insert_after='vat_section', print_hide=1), - dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable', + dict(fieldname='reverse_charge', label='Reverse Charge Applicable', fieldtype='Select', insert_after='permit_no', print_hide=1, options='Y\nN', default='N') ] @@ -42,7 +42,7 @@ def make_custom_fields(): fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='emirate', label='Emirate', insert_after='customer_address', - fetch_from='customer_address.emirates'), + fieldtype='Read Only', fetch_from='customer_address.emirates'), ] invoice_item_fields = [ @@ -79,8 +79,8 @@ def make_custom_fields(): fieldtype='Data', insert_after='supplier_name'), ], 'Address': [ - dict(fieldname='emirates', label='Emirates', - fieldtype='Data', insert_after='state'), + dict(fieldname='emirates', label='Emirates', fieldtype='Select', insert_after='state', + options='Abu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, From 323791d123e63e9a7b61109d7a727137e7fe81d0 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 10 Sep 2020 17:53:29 +0530 Subject: [PATCH 014/283] refactor: added docstrings --- erpnext/regional/report/uae_vat/uae_vat.py | 75 +++++++++++++++++++--- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py index 50d1ddd351..4c4c6bc2ab 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.py +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -12,6 +12,11 @@ def execute(filters=None): return columns, data def get_columns(): + """Creates a list of dictionaries that are used to generate column headers of the data table + + Returns: + List(Dict): list of dictionaries that are used to generate column headers of the data table + """ return [ { "fieldname": "no", @@ -46,6 +51,14 @@ def get_columns(): ] def get_data(filters = None): + """Returns the list of dictionaries. Each dictionary is a row in the datatable + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + List(Dict): Each dictionary is a row in the datatable + """ data = [] total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() @@ -90,6 +103,11 @@ def get_total_emiratewise(filters): """, filters) def get_emirates(): + """Returns a List of emirates in the order that they are to be displayed + + Returns: + List(String): List of emirates in the order that they are to be displayed + """ return [ 'Abu Dhabi', 'Dubai', @@ -101,6 +119,14 @@ def get_emirates(): ] def get_conditions(filters): + """The conditions to be used to filter data to calculate the total sale + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + String: Concatenated list of conditions to be applied to calculate the total sale + """ conditions = "" for opts in (("company", f' and company="{filters.get("company")}"'), ("from_date", f' and posting_date>="{filters.get("from_date")}"'), @@ -109,7 +135,40 @@ def get_conditions(filters): conditions += opts[1] return conditions +def get_reverse_charge_total(filters): + """Returns the sum of the total of each Purchase invoice made + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the total of each Purchase invoice made + """ + conditions = """ + for opts in (("company", f' and company="{filters.get("company")}"'), + ("from_date", f' and posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + if filters.get(opts[0]): + conditions += opts[1] + return conditions + """ + return frappe.db.sql(f""" + select sum(total) from + `tabPurchase Invoice` + where + reverse_charge = "Y" + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + def get_reverse_charge_tax(filters): + """Returns the sum of the tax of each Purchase invoice made + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the tax of each Purchase invoice made + """ return frappe.db.sql(f""" select sum(debit) from `tabPurchase Invoice` inner join `tabGL Entry` @@ -122,16 +181,16 @@ def get_reverse_charge_tax(filters): """)[0][0] -def get_reverse_charge_total(filters): - return frappe.db.sql(f""" - select sum(total) from - `tabPurchase Invoice` - where - reverse_charge = "Y" - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] def get_conditions_join(filters): + """The conditions to be used to filter data to calculate the total vat + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + String: Concatenated list of conditions to be applied to calculate the total vat + """ conditions = "" for opts in (("company", f' and `tabPurchase Invoice`.company="{filters.get("company")}"'), ("from_date", f' and `tabPurchase Invoice`.posting_date>="{filters.get("from_date")}"'), From f44524dd3e3ed87ed112d99ea7fef8cf450c9dcd Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 10 Sep 2020 18:35:04 +0530 Subject: [PATCH 015/283] feat: Added a Chart to compare Vat and sales between emirates --- erpnext/regional/report/uae_vat/uae_vat.py | 48 +++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py index 4c4c6bc2ab..8eeff51723 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.py +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -4,12 +4,12 @@ from __future__ import unicode_literals import frappe from erpnext.regional.united_arab_emirates.utils import get_tax_accounts +from frappe import _ def execute(filters=None): columns = get_columns() - data = get_data(filters) - - return columns, data + data, chart = get_data(filters) + return columns, data, None, chart def get_columns(): """Creates a list of dictionaries that are used to generate column headers of the data table @@ -51,13 +51,14 @@ def get_columns(): ] def get_data(filters = None): - """Returns the list of dictionaries. Each dictionary is a row in the datatable + """Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data Args: filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. Returns: List(Dict): Each dictionary is a row in the datatable + Dict: Dictionary containing chart data """ data = [] total_emiratewise = get_total_emiratewise(filters) @@ -70,6 +71,9 @@ def get_data(filters = None): "amount": amount, "vat_amount": vat } + + chart = get_chart_data(emirates, amounts_by_emirate) + for d, emirate in enumerate(emirates, 97): if emirate in amounts_by_emirate: amounts_by_emirate[emirate]["no"] = f'1{chr(d)}' @@ -92,9 +96,43 @@ def get_data(filters = None): "vat_amount": get_reverse_charge_tax(filters) } ) - return data + return data, chart +def get_chart_data(emirates, amounts_by_emirate): + """Returns chart data + + Args: + emirates (List): List of Emirates + amounts_by_emirate (Dict): Vat and Tax amount by emirates with emirates as key + + Returns: + [Dict]: Chart Data + """ + labels = [] + amount = [] + vat_amount = [] + for d in emirates: + if d in amounts_by_emirate: + amount.append(amounts_by_emirate[d]["amount"]) + vat_amount.append(amounts_by_emirate[d]["vat_amount"]) + labels.append(d) + + datasets = [] + datasets.append({'name': _('Amount (AED)'), 'values': amount}) + datasets.append({'name': _('Vat Amount (AED)'), 'values': vat_amount}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + } + } + + chart["type"] = "bar" + chart["fieldtype"] = "Currency" + return chart + def get_total_emiratewise(filters): return frappe.db.sql(f""" select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` From 3ef11b1d647666b2eed836fd7f449e1b1bb88f8f Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 24 Sep 2020 13:21:23 +0530 Subject: [PATCH 016/283] feat(UAE VAT 21): Add region fields for UAE VAT 21 --- erpnext/hooks.py | 3 +++ .../regional/united_arab_emirates/setup.py | 20 +++++++++++++++---- .../regional/united_arab_emirates/utils.py | 12 ++++++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 72be2dcc75..ec34c4e126 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -243,6 +243,9 @@ doc_events = { "erpnext.regional.italy.utils.sales_invoice_on_cancel", "erpnext.erpnext_integrations.taxjar_integration.delete_transaction" ], + "validate": [ + "erpnext.regional.united_arab_emirates.utils.validate_returns", + ], "on_trash": "erpnext.regional.check_deletion_permission" }, "Purchase Invoice": { diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index d38d647277..f116303d80 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -20,9 +20,6 @@ def make_custom_fields(): insert_after='group_same_items', print_hide=1, collapsible=1), dict(fieldname='permit_no', label='Permit Number', fieldtype='Data', insert_after='vat_section', print_hide=1), - dict(fieldname='reverse_charge', label='Reverse Charge Applicable', - fieldtype='Select', insert_after='permit_no', print_hide=1, - options='Y\nN', default='N') ] purchase_invoice_fields = [ @@ -31,7 +28,12 @@ def make_custom_fields(): fetch_from='company.tax_id', print_hide=1), dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', - fetch_from='supplier.supplier_name_in_arabic', print_hide=1) + fetch_from='supplier.supplier_name_in_arabic', print_hide=1), + dict(fieldname='reverse_charge', label='Reverse Charge Applicable', + fieldtype='Select', insert_after='permit_no', print_hide=1, + options='Y\nN', default='N'), + dict(fieldname='claimable_reverse_charge', label='Claimable Reverse Charge (Percentage)', + insert_after='reverse_charge', fieldtype='Percent'), ] sales_invoice_fields = [ @@ -43,6 +45,12 @@ def make_custom_fields(): fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='emirate', label='Emirate', insert_after='customer_address', fieldtype='Read Only', fetch_from='customer_address.emirates'), + dict(fieldname='returns_column_break', fieldtype='Column Break', + insert_after='select_print_heading'), + dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', + insert_after='returns_column_break', fieldtype='Currency',), + dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', + insert_after='tourist_tax_return', fieldtype='Currency',), ] invoice_item_fields = [ @@ -69,6 +77,10 @@ def make_custom_fields(): 'Item': [ dict(fieldname='tax_code', label='Tax Code', fieldtype='Data', insert_after='item_group'), + # dict(fieldname='is_zero_rated', label='Is Zero Rated', + # fieldtype='Check', insert_after='tax_code'), + # dict(fieldname='is_exempt', label='Is Exempt ', + # fieldtype='Check', insert_after='is_zero_rated') ], 'Customer': [ dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 05a99d190a..5242c63e63 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -166,4 +166,14 @@ def make_regional_gl_entries(gl_entries, doc): else tax.tax_amount_after_discount_amount }, account_currency, item=tax) ) - return gl_entries \ No newline at end of file + return gl_entries + +def validate_returns(doc, method): + print("validate_returns") + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'United Arab Emirates': + return + + if flt(doc.tourist_tax_return) + flt(doc.standard_rated_expenses) > flt(doc.total_taxes_and_charges): + frappe.throw(_("The Total Returns(Tax Refund provided to Tourists (AED) + Standard Rated Expenses (AED)) should be less than the Total Taxes and Charges (Company Currency)")) \ No newline at end of file From a119688e32f0a56ae546d75cdd047b2190308fa7 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 24 Sep 2020 15:22:46 +0530 Subject: [PATCH 017/283] fix: (RFQ/SQ) Link to Material Requests in Tools section --- .../request_for_quotation.js | 5 + .../request_for_quotation.json | 9 +- .../supplier_quotation/supplier_quotation.js | 6 + .../supplier_quotation.json | 10 +- erpnext/public/js/controllers/buying.js | 125 +++++++++--------- 5 files changed, 76 insertions(+), 79 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 4a937f7f0d..660af96505 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -326,6 +326,11 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e d.show(); }, __("Get items from")); + //Link Material Requests + this.frm.add_custom_button(__('Link to Material Requests'), + function() { + erpnext.buying.link_to_mrs(me.frm); + }, __("Tools")); } }, diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 5cd8e6f4fa..6994ef72f0 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -17,7 +17,6 @@ "get_suppliers_button", "items_section", "items", - "link_to_mrs", "supplier_response_section", "email_template", "message_for_supplier", @@ -119,12 +118,6 @@ "options": "Request for Quotation Item", "reqd": 1 }, - { - "depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)", - "fieldname": "link_to_mrs", - "fieldtype": "Button", - "label": "Link to Material Requests" - }, { "fieldname": "supplier_response_section", "fieldtype": "Section Break" @@ -235,7 +228,7 @@ "icon": "fa fa-shopping-cart", "is_submittable": 1, "links": [], - "modified": "2020-06-25 14:37:21.140194", + "modified": "2020-09-24 13:53:56.066616", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 1b8b40459f..c146f13dfe 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -54,6 +54,12 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext } }) }, __("Get items from")); + + //Link Material Requests + this.frm.add_custom_button(__('Link to Material Requests'), + function() { + erpnext.buying.link_to_mrs(me.frm); + }, __("Tools")); } }, diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 660dcff34b..0ffaa44a0d 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -34,7 +34,6 @@ "ignore_pricing_rule", "items_section", "items", - "link_to_mrs", "pricing_rule_details", "pricing_rules", "section_break_22", @@ -320,12 +319,6 @@ "options": "Supplier Quotation Item", "reqd": 1 }, - { - "depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)", - "fieldname": "link_to_mrs", - "fieldtype": "Button", - "label": "Link to material requests" - }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", @@ -803,9 +796,10 @@ ], "icon": "fa fa-shopping-cart", "idx": 29, + "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:10:45.556792", + "modified": "2020-09-24 15:18:29.073368", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cb76c87b62..fb904e7d66 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -294,69 +294,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.get_terms(); }, - link_to_mrs: function() { - var my_items = []; - for (var i in cur_frm.doc.items) { - if(!cur_frm.doc.items[i].material_request){ - my_items.push(cur_frm.doc.items[i].item_code); - } - } - frappe.call({ - method: "erpnext.buying.utils.get_linked_material_requests", - args:{ - items: my_items - }, - callback: function(r) { - if(!r.message || r.message.length == 0) { - frappe.throw(__("No pending Material Requests found to link for the given items.")) - } - else { - var i = 0; - var item_length = cur_frm.doc.items.length; - while (i < item_length) { - var qty = cur_frm.doc.items[i].qty; - (r.message[0] || []).forEach(function(d) { - if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item) - { - cur_frm.doc.items[i].material_request = d.mr_name; - cur_frm.doc.items[i].material_request_item = d.mr_item; - var my_qty = Math.min(qty, d.qty); - qty = qty - my_qty; - d.qty = d.qty - my_qty; - cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor; - cur_frm.doc.items[i].qty = my_qty; - - frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")"); - if (qty > 0) - { - frappe.msgprint("Splitting " + qty + " units of " + d.item_code); - var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items"); - item_length++; - - for (var key in cur_frm.doc.items[i]) - { - newrow[key] = cur_frm.doc.items[i][key]; - } - - newrow.idx = item_length; - newrow["stock_qty"] = newrow.conversion_factor*qty; - newrow["qty"] = qty; - - newrow["material_request"] = ""; - newrow["material_request_item"] = ""; - - } - } - }); - i++; - } - refresh_field("items"); - //cur_frm.save(); - } - } - }); - }, - update_auto_repeat_reference: function(doc) { if (doc.auto_repeat) { frappe.call({ @@ -422,6 +359,68 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ cur_frm.add_fetch('project', 'cost_center', 'cost_center'); +erpnext.buying.link_to_mrs = function(frm) { + var my_items = []; + for (var i in frm.doc.items) { + if(!frm.doc.items[i].material_request){ + my_items.push(frm.doc.items[i].item_code); + } + } + frappe.call({ + method: "erpnext.buying.utils.get_linked_material_requests", + args:{ + items: my_items + }, + callback: function(r) { + if(!r.message || r.message.length == 0) { + frappe.throw(__("No pending Material Requests found to link for the given items.")) + } + else { + var i = 0; + var item_length = frm.doc.items.length; + while (i < item_length) { + var qty = frm.doc.items[i].qty; + (r.message[0] || []).forEach(function(d) { + if (d.qty > 0 && qty > 0 && frm.doc.items[i].item_code == d.item_code && !frm.doc.items[i].material_request_item) + { + frm.doc.items[i].material_request = d.mr_name; + frm.doc.items[i].material_request_item = d.mr_item; + var my_qty = Math.min(qty, d.qty); + qty = qty - my_qty; + d.qty = d.qty - my_qty; + frm.doc.items[i].stock_qty = my_qty*frm.doc.items[i].conversion_factor; + frm.doc.items[i].qty = my_qty; + + frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + frm.doc.items[i].idx + ")"); + if (qty > 0) + { + frappe.msgprint("Splitting " + qty + " units of " + d.item_code); + var newrow = frappe.model.add_child(frm.doc, frm.doc.items[i].doctype, "items"); + item_length++; + + for (var key in frm.doc.items[i]) + { + newrow[key] = frm.doc.items[i][key]; + } + + newrow.idx = item_length; + newrow["stock_qty"] = newrow.conversion_factor*qty; + newrow["qty"] = qty; + + newrow["material_request"] = ""; + newrow["material_request_item"] = ""; + + } + } + }); + i++; + } + refresh_field("items"); + } + } + }); +} + erpnext.buying.get_default_bom = function(frm) { $.each(frm.doc["items"] || [], function(i, d) { if (d.item_code && d.bom === "") { From be4dbad823ecba34fbedccca84f351fb08ccf2fa Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 24 Sep 2020 16:43:25 +0530 Subject: [PATCH 018/283] style: (minor) Reduce loc --- erpnext/public/js/controllers/buying.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index fb904e7d66..62da7f5c5b 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -360,20 +360,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ cur_frm.add_fetch('project', 'cost_center', 'cost_center'); erpnext.buying.link_to_mrs = function(frm) { - var my_items = []; - for (var i in frm.doc.items) { - if(!frm.doc.items[i].material_request){ - my_items.push(frm.doc.items[i].item_code); - } - } frappe.call({ method: "erpnext.buying.utils.get_linked_material_requests", args:{ - items: my_items + items: frm.doc.items.map((item) => {return item.item_code;}) }, callback: function(r) { if(!r.message || r.message.length == 0) { - frappe.throw(__("No pending Material Requests found to link for the given items.")) + frappe.throw({message: __("No pending Material Requests found to link for the given items."), title: __("Note")}); } else { var i = 0; From 5225215d3116cefd45f5c1ed7a7fce2fddab9522 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 24 Sep 2020 18:41:43 +0530 Subject: [PATCH 019/283] feat(UAE VAT 21): Add rows for inputs and tourists --- erpnext/regional/report/uae_vat/uae_vat.py | 168 +++++++++++++++++- .../regional/united_arab_emirates/setup.py | 11 +- 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py index 8eeff51723..b3e52be3e5 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.py +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -61,6 +61,7 @@ def get_data(filters = None): Dict: Dictionary containing chart data """ data = [] + data.append({"legend": f'VAT on Sales and All Other Outputs',}) total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} @@ -88,6 +89,16 @@ def get_data(filters = None): "vat_amount": 0 } ) + + data.append( + { + "no": '2', + "legend": f'Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme', + "amount": (-1) * get_tourist_tax_return_total(filters), + "vat_amount": (-1) * get_tourist_tax_return_tax(filters) + } + ) + data.append( { "no": '3', @@ -96,6 +107,25 @@ def get_data(filters = None): "vat_amount": get_reverse_charge_tax(filters) } ) + + data.append({"legend": f'VAT on Expenses and All Other Inputs'}) + data.append( + { + "no": '9', + "legend": f'Standard Rated Expenses', + "amount": get_standard_rated_expenses_total(filters), + "vat_amount": get_standard_rated_expenses_tax(filters) + } + ) + data.append( + { + "no": '10', + "legend": f'Supplies subject to the reverse charge provision', + "amount": get_reverse_charge_recoverable_total(filters), + "vat_amount": get_reverse_charge_recoverable_tax(filters) + } + ) + return data, chart @@ -235,4 +265,140 @@ def get_conditions_join(filters): ("to_date", f' and `tabPurchase Invoice`.posting_date<="{filters.get("to_date")}"')): if filters.get(opts[0]): conditions += opts[1] - return conditions \ No newline at end of file + return conditions + + +def get_reverse_charge_recoverable_total(filters): + """Returns the sum of the total of each Purchase invoice made with claimable reverse charge + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the total of each Purchase invoice made with claimable reverse charge + """ + conditions = """ + for opts in (("company", f' and company="{filters.get("company")}"'), + ("from_date", f' and posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + if filters.get(opts[0]): + conditions += opts[1] + return conditions + """ + return frappe.db.sql(f""" + select sum(total) from + `tabPurchase Invoice` + where + reverse_charge = "Y" + and claimable_reverse_charge > 0 + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + + +def get_reverse_charge_recoverable_tax(filters): + """Returns the sum of the tax of each Purchase invoice made + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the tax of each Purchase invoice made + """ + return frappe.db.sql(f""" + select sum(debit * `tabPurchase Invoice`.claimable_reverse_charge / 100) from + `tabPurchase Invoice` inner join `tabGL Entry` + on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name + where + `tabPurchase Invoice`.reverse_charge = "Y" + and `tabPurchase Invoice`.docstatus = 1 + and `tabPurchase Invoice`.claimable_reverse_charge > 0 + and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} + and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); + """)[0][0] + + +def get_standard_rated_expenses_total(filters): + """Returns the sum of the total of each Purchase invoice made with claimable reverse charge + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the total of each Purchase invoice made with claimable reverse charge + """ + conditions = """ + for opts in (("company", f' and company="{filters.get("company")}"'), + ("from_date", f' and posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + if filters.get(opts[0]): + conditions += opts[1] + return conditions + """ + return frappe.db.sql(f""" + select sum(total) from + `tabSales Invoice` + where + standard_rated_expenses > 0 + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + + +def get_standard_rated_expenses_tax(filters): + """Returns the sum of the tax of each Purchase invoice made + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the tax of each Purchase invoice made + """ + return frappe.db.sql(f""" + select sum(standard_rated_expenses) from + `tabSales Invoice` + where + standard_rated_expenses > 0 + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + +def get_tourist_tax_return_total(filters): + """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the total of each Sales invoice with non zero tourist_tax_return + """ + conditions = """ + for opts in (("company", f' and company="{filters.get("company")}"'), + ("from_date", f' and posting_date>="{filters.get("from_date")}"'), + ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + if filters.get(opts[0]): + conditions += opts[1] + return conditions + """ + return frappe.db.sql(f""" + select sum(total) from + `tabSales Invoice` + where + tourist_tax_return > 0 + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] + + +def get_tourist_tax_return_tax(filters): + """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of the tax of each Sales invoice with non zero tourist_tax_return + """ + return frappe.db.sql(f""" + select sum(tourist_tax_return) from + `tabSales Invoice` + where + tourist_tax_return > 0 + and docstatus = 1 {get_conditions(filters)} ; + """)[0][0] \ No newline at end of file diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index f116303d80..01763402b1 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -33,7 +33,8 @@ def make_custom_fields(): fieldtype='Select', insert_after='permit_no', print_hide=1, options='Y\nN', default='N'), dict(fieldname='claimable_reverse_charge', label='Claimable Reverse Charge (Percentage)', - insert_after='reverse_charge', fieldtype='Percent'), + insert_after='reverse_charge', fieldtype='Percent', print_hide=1, + depends_on="eval:doc.reverse_charge=='Y'", default='100.000'), ] sales_invoice_fields = [ @@ -45,12 +46,12 @@ def make_custom_fields(): fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='emirate', label='Emirate', insert_after='customer_address', fieldtype='Read Only', fetch_from='customer_address.emirates'), - dict(fieldname='returns_column_break', fieldtype='Column Break', - insert_after='select_print_heading'), + # dict(fieldname='returns_column_break', fieldtype='Column Break', + # insert_after='select_print_heading'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', - insert_after='returns_column_break', fieldtype='Currency',), + insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', - insert_after='tourist_tax_return', fieldtype='Currency',), + insert_after='tourist_tax_return', fieldtype='Currency', print_hide=1, default='0'), ] invoice_item_fields = [ From 72cad2a8abd173b2c84029bfdff6df38d6e87a6f Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Fri, 25 Sep 2020 13:13:38 +0530 Subject: [PATCH 020/283] feat(UAE VAT 21): Add zero rated and vat exempt --- erpnext/regional/report/uae_vat/uae_vat.js | 12 ++- erpnext/regional/report/uae_vat/uae_vat.py | 87 +++++++++++++++---- .../regional/united_arab_emirates/setup.py | 21 +++-- 3 files changed, 96 insertions(+), 24 deletions(-) diff --git a/erpnext/regional/report/uae_vat/uae_vat.js b/erpnext/regional/report/uae_vat/uae_vat.js index 45df167823..0213956d44 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.js +++ b/erpnext/regional/report/uae_vat/uae_vat.js @@ -27,5 +27,15 @@ frappe.query_reports["UAE VAT"] = { "reqd": 1, "default": frappe.datetime.get_today() }, - ] + ], + "formatter": function(value, row, column, data, default_formatter) { + if (data + && (data.legend=='VAT on Sales and All Other Outputs' || data.legend=='VAT on Expenses and All Other Inputs') + && data.legend==value) { + value = $(`${value}`); + var $value = $(value).css("font-weight", "bold"); + value = $value.wrap("

").parent().html(); + } + return value; + }, }; diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat/uae_vat.py index b3e52be3e5..1a3537c268 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.py +++ b/erpnext/regional/report/uae_vat/uae_vat.py @@ -41,12 +41,6 @@ def get_columns(): "label": "VAT Amount (AED)", "fieldtype": "Currency", "width": 100 - }, - { - "fieldname": "adjustment", - "label": "Adjustment (AED)", - "fieldtype": "Currency", - "width": 100 } ] @@ -61,7 +55,12 @@ def get_data(filters = None): Dict: Dictionary containing chart data """ data = [] - data.append({"legend": f'VAT on Sales and All Other Outputs',}) + data.append({ + "no": '', + "legend": f'VAT on Sales and All Other Outputs', + "amount": '', + "vat_amount": '' + }) total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} @@ -108,7 +107,30 @@ def get_data(filters = None): } ) - data.append({"legend": f'VAT on Expenses and All Other Inputs'}) + data.append( + { + "no": '4', + "legend": f'Zero Rated', + "amount": get_zero_rated_total(filters), + "vat_amount": "-" + } + ) + + data.append( + { + "no": '5', + "legend": f'Exempt Supplies', + "amount": get_exempt_total(filters), + "vat_amount": "-" + } + ) + + data.append({ + "no": '', + "legend": f'VAT on Expenses and All Other Inputs', + "amount": '', + "vat_amount": '' + }) data.append( { "no": '9', @@ -226,7 +248,7 @@ def get_reverse_charge_total(filters): where reverse_charge = "Y" and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] + """)[0][0] or 0 def get_reverse_charge_tax(filters): """Returns the sum of the tax of each Purchase invoice made @@ -246,7 +268,7 @@ def get_reverse_charge_tax(filters): and `tabPurchase Invoice`.docstatus = 1 and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); - """)[0][0] + """)[0][0] or 0 @@ -292,7 +314,7 @@ def get_reverse_charge_recoverable_total(filters): reverse_charge = "Y" and claimable_reverse_charge > 0 and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] + """)[0][0] or 0 def get_reverse_charge_recoverable_tax(filters): @@ -314,7 +336,7 @@ def get_reverse_charge_recoverable_tax(filters): and `tabPurchase Invoice`.claimable_reverse_charge > 0 and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); - """)[0][0] + """)[0][0] or 0 def get_standard_rated_expenses_total(filters): @@ -340,7 +362,7 @@ def get_standard_rated_expenses_total(filters): where standard_rated_expenses > 0 and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] + """)[0][0] or 0 def get_standard_rated_expenses_tax(filters): @@ -358,7 +380,7 @@ def get_standard_rated_expenses_tax(filters): where standard_rated_expenses > 0 and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] + """)[0][0] or 0 def get_tourist_tax_return_total(filters): """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return @@ -383,7 +405,7 @@ def get_tourist_tax_return_total(filters): where tourist_tax_return > 0 and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] + """)[0][0] or 0 def get_tourist_tax_return_tax(filters): @@ -401,4 +423,37 @@ def get_tourist_tax_return_tax(filters): where tourist_tax_return > 0 and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] \ No newline at end of file + """)[0][0] or 0 + +def get_zero_rated_total(filters): + """Returns the sum of each Sales Invoice Item Amount which is zero rated + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of each Sales Invoice Item Amount which is zero rated + """ + return frappe.db.sql(f""" + select sum(i.base_amount) as total from + `tabSales Invoice Item` i, `tabSales Invoice` s + where s.docstatus = 1 and i.parent = s.name and i.is_zero_rated = 1 + {get_conditions(filters)} ; + """)[0][0] or 0 + + +def get_exempt_total(filters): + """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt + + Args: + filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. + + Returns: + Float: sum of each Sales Invoice Item Amount which is Vat Exempt + """ + return frappe.db.sql(f""" + select sum(i.base_amount) as total from + `tabSales Invoice Item` i, `tabSales Invoice` s + where s.docstatus = 1 and i.parent = s.name and i.is_exempt = 1 + {get_conditions(filters)} ; + """)[0][0] or 0 \ No newline at end of file diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 01763402b1..29ad41c9af 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -15,6 +15,13 @@ def setup(company=None, patch=True): create_sales_tax(company) def make_custom_fields(): + is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', fetch_from='item_code.is_zero_rated', insert_after='description', + print_hide=1) + is_exempt = dict(fieldname='is_exempt', label='Is Exempt', + fieldtype='Check', fetch_from='item_code.is_exempt', insert_after='is_zero_rated', + print_hide=1) + invoice_fields = [ dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break', insert_after='group_same_items', print_hide=1, collapsible=1), @@ -46,8 +53,6 @@ def make_custom_fields(): fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='emirate', label='Emirate', insert_after='customer_address', fieldtype='Read Only', fetch_from='customer_address.emirates'), - # dict(fieldname='returns_column_break', fieldtype='Column Break', - # insert_after='select_print_heading'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', @@ -78,10 +83,12 @@ def make_custom_fields(): 'Item': [ dict(fieldname='tax_code', label='Tax Code', fieldtype='Data', insert_after='item_group'), - # dict(fieldname='is_zero_rated', label='Is Zero Rated', - # fieldtype='Check', insert_after='tax_code'), - # dict(fieldname='is_exempt', label='Is Exempt ', - # fieldtype='Check', insert_after='is_zero_rated') + dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', insert_after='tax_code', + print_hide=1), + dict(fieldname='is_exempt', label='Is Exempt ', + fieldtype='Check', insert_after='is_zero_rated', + print_hide=1) ], 'Customer': [ dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', @@ -101,7 +108,7 @@ def make_custom_fields(): 'Sales Invoice': sales_invoice_fields + invoice_fields, 'Sales Order': sales_invoice_fields + invoice_fields, 'Delivery Note': sales_invoice_fields + invoice_fields, - 'Sales Invoice Item': invoice_item_fields + delivery_date_field, + 'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt], 'Purchase Invoice Item': invoice_item_fields, 'Sales Order Item': invoice_item_fields, 'Delivery Note Item': invoice_item_fields, From 321b5f54aef19c66460657552695af65b4a977a6 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Fri, 25 Sep 2020 14:24:57 +0530 Subject: [PATCH 021/283] feat(UAE VAT 21): Add Roles for UAE VAT 21 Report --- erpnext/regional/united_arab_emirates/setup.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 29ad41c9af..95fde3ab9f 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -10,6 +10,7 @@ from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax def setup(company=None, patch=True): make_custom_fields() add_print_formats() + add_custom_roles_for_reports() if company: create_sales_tax(company) @@ -127,3 +128,15 @@ def add_print_formats(): frappe.db.sql(""" update `tabPrint Format` set disabled = 0 where name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """) + +def add_custom_roles_for_reports(): + if not frappe.db.get_value('Custom Role', dict(report='UAE VAT')): + frappe.get_doc(dict( + doctype='Custom Role', + report='UAE VAT', + roles= [ + dict(role='Accounts User'), + dict(role='Accounts Manager'), + dict(role='Auditor') + ] + )).insert() \ No newline at end of file From 127fbfcb931f4a7ed215a6102dfa3e8ff18155f7 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Fri, 25 Sep 2020 20:42:54 +0530 Subject: [PATCH 022/283] feat(UAE VAT 21): Added Print Format --- erpnext/regional/report/uae_vat/uae_vat.html | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 erpnext/regional/report/uae_vat/uae_vat.html diff --git a/erpnext/regional/report/uae_vat/uae_vat.html b/erpnext/regional/report/uae_vat/uae_vat.html new file mode 100644 index 0000000000..861c0c818d --- /dev/null +++ b/erpnext/regional/report/uae_vat/uae_vat.html @@ -0,0 +1,68 @@ +{% + var report_columns = report.get_columns_for_print(); + report_columns = report_columns.filter(col => !col.hidden); + + if (report_columns.length > 8) { + frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application.")); + } +%} + +

{%= __(report.report_name) %}

+ +

{%= __("VAT on Sales and All Other Outputs") %}

+ + + + + {% for (let i=0; i{%= report_columns[i].label %} + {% } %} + + + + {% for (let j=1; j<12; j++) { %} + {% + var row = data[j]; + %} + + {% for (let i=0; i + {% const fieldname = report_columns[i].fieldname; %} + {% if (!is_null(row[fieldname])) { %} + {%= frappe.format(row[fieldname], report_columns[i], {}, row) %} + {% } %} + + {% } %} + + {% } %} + +
+ +

{%= __("VAT on Expenses and All Other Inputs") %}

+ + + + {% for (let i=0; i{%= report_columns[i].label %} + {% } %} + + + + {% for (let j=12; j + {% for (let i=0; i + {% const fieldname = report_columns[i].fieldname; %} + {% if (!is_null(row[fieldname])) { %} + {%= frappe.format(row[fieldname], report_columns[i], {}, row) %} + {% } %} + + {% } %} + + {% } %} + + +
\ No newline at end of file From 79aca28453c2eb5a7d8c917459269c2efcb0a96d Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 12:27:41 +0530 Subject: [PATCH 023/283] refactor(UAE VAT 21): rename UAE VAT - UAE VAT 21 --- erpnext/regional/report/{uae_vat => uae_vat_21}/__init__.py | 0 .../{uae_vat/uae_vat.html => uae_vat_21/uae_vat_21.html} | 0 .../report/{uae_vat/uae_vat.js => uae_vat_21/uae_vat_21.js} | 2 +- .../{uae_vat/uae_vat.json => uae_vat_21/uae_vat_21.json} | 4 ++-- .../report/{uae_vat/uae_vat.py => uae_vat_21/uae_vat_21.py} | 0 erpnext/regional/united_arab_emirates/setup.py | 4 ++-- 6 files changed, 5 insertions(+), 5 deletions(-) rename erpnext/regional/report/{uae_vat => uae_vat_21}/__init__.py (100%) rename erpnext/regional/report/{uae_vat/uae_vat.html => uae_vat_21/uae_vat_21.html} (100%) rename erpnext/regional/report/{uae_vat/uae_vat.js => uae_vat_21/uae_vat_21.js} (96%) rename erpnext/regional/report/{uae_vat/uae_vat.json => uae_vat_21/uae_vat_21.json} (89%) rename erpnext/regional/report/{uae_vat/uae_vat.py => uae_vat_21/uae_vat_21.py} (100%) diff --git a/erpnext/regional/report/uae_vat/__init__.py b/erpnext/regional/report/uae_vat_21/__init__.py similarity index 100% rename from erpnext/regional/report/uae_vat/__init__.py rename to erpnext/regional/report/uae_vat_21/__init__.py diff --git a/erpnext/regional/report/uae_vat/uae_vat.html b/erpnext/regional/report/uae_vat_21/uae_vat_21.html similarity index 100% rename from erpnext/regional/report/uae_vat/uae_vat.html rename to erpnext/regional/report/uae_vat_21/uae_vat_21.html diff --git a/erpnext/regional/report/uae_vat/uae_vat.js b/erpnext/regional/report/uae_vat_21/uae_vat_21.js similarity index 96% rename from erpnext/regional/report/uae_vat/uae_vat.js rename to erpnext/regional/report/uae_vat_21/uae_vat_21.js index 0213956d44..3e6cdd85e5 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.js +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.js @@ -2,7 +2,7 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports["UAE VAT"] = { +frappe.query_reports["UAE VAT 21"] = { "filters": [ { "fieldname": "company", diff --git a/erpnext/regional/report/uae_vat/uae_vat.json b/erpnext/regional/report/uae_vat_21/uae_vat_21.json similarity index 89% rename from erpnext/regional/report/uae_vat/uae_vat.json rename to erpnext/regional/report/uae_vat_21/uae_vat_21.json index 8807405a98..421990cc4a 100644 --- a/erpnext/regional/report/uae_vat/uae_vat.json +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.json @@ -12,11 +12,11 @@ "modified": "2020-09-10 08:51:02.298482", "modified_by": "Administrator", "module": "Regional", - "name": "UAE VAT", + "name": "UAE VAT 21", "owner": "Administrator", "prepared_report": 0, "ref_doctype": "GL Entry", - "report_name": "UAE VAT", + "report_name": "UAE VAT 21", "report_type": "Script Report", "roles": [] } \ No newline at end of file diff --git a/erpnext/regional/report/uae_vat/uae_vat.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py similarity index 100% rename from erpnext/regional/report/uae_vat/uae_vat.py rename to erpnext/regional/report/uae_vat_21/uae_vat_21.py diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 95fde3ab9f..e7a0609f4a 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -130,10 +130,10 @@ def add_print_formats(): name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """) def add_custom_roles_for_reports(): - if not frappe.db.get_value('Custom Role', dict(report='UAE VAT')): + if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 21')): frappe.get_doc(dict( doctype='Custom Role', - report='UAE VAT', + report='UAE VAT 21', roles= [ dict(role='Accounts User'), dict(role='Accounts Manager'), From 815b7584dddbe4c29cc453c98d80d697035103c7 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 13:24:40 +0530 Subject: [PATCH 024/283] refactor(UAE VAT 21): seperate get_chart, get_data --- erpnext/regional/report/uae_vat_21/uae_vat_21.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py index 1a3537c268..d21cecd826 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.py @@ -8,7 +8,9 @@ from frappe import _ def execute(filters=None): columns = get_columns() - data, chart = get_data(filters) + data, emirates, amounts_by_emirate = get_data(filters) + chart = get_chart(emirates, amounts_by_emirate) + return columns, data, None, chart def get_columns(): @@ -72,8 +74,6 @@ def get_data(filters = None): "vat_amount": vat } - chart = get_chart_data(emirates, amounts_by_emirate) - for d, emirate in enumerate(emirates, 97): if emirate in amounts_by_emirate: amounts_by_emirate[emirate]["no"] = f'1{chr(d)}' @@ -148,10 +148,10 @@ def get_data(filters = None): } ) - return data, chart + return data, emirates, amounts_by_emirate -def get_chart_data(emirates, amounts_by_emirate): +def get_chart(emirates, amounts_by_emirate): """Returns chart data Args: From bed1add349cf571a912f8956ba7d86b91f48cfbe Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 13:26:05 +0530 Subject: [PATCH 025/283] docs(UAE VAT 21): add docstrings --- erpnext/regional/united_arab_emirates/setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index e7a0609f4a..07543b31ea 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -130,6 +130,8 @@ def add_print_formats(): name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """) def add_custom_roles_for_reports(): + """Add Access Control to UAE VAT 21 + """ if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 21')): frappe.get_doc(dict( doctype='Custom Role', From 2a601e59bfb78d74551fb02af82fb17d8acef725 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 13:26:43 +0530 Subject: [PATCH 026/283] fix(UAE VAT 21): remove extra legend --- erpnext/regional/report/uae_vat_21/uae_vat_21.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.html b/erpnext/regional/report/uae_vat_21/uae_vat_21.html index 861c0c818d..aa6a255932 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.html +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.html @@ -48,7 +48,7 @@ - {% for (let j=12; j Date: Mon, 28 Sep 2020 14:38:14 +0530 Subject: [PATCH 027/283] feat(UAE VAT 21): Add vat settings --- .../doctype/uae_vat_account/__init__.py | 0 .../uae_vat_account/uae_vat_account.json | 35 ++++++++++++ .../uae_vat_account/uae_vat_account.py | 10 ++++ .../doctype/uae_vat_setting/__init__.py | 0 .../uae_vat_setting/test_uae_vat_setting.py | 10 ++++ .../uae_vat_setting/uae_vat_setting.js | 8 +++ .../uae_vat_setting/uae_vat_setting.json | 55 +++++++++++++++++++ .../uae_vat_setting/uae_vat_setting.py | 10 ++++ .../regional/united_arab_emirates/utils.py | 9 +-- 9 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 erpnext/regional/doctype/uae_vat_account/__init__.py create mode 100644 erpnext/regional/doctype/uae_vat_account/uae_vat_account.json create mode 100644 erpnext/regional/doctype/uae_vat_account/uae_vat_account.py create mode 100644 erpnext/regional/doctype/uae_vat_setting/__init__.py create mode 100644 erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py create mode 100644 erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js create mode 100644 erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json create mode 100644 erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py diff --git a/erpnext/regional/doctype/uae_vat_account/__init__.py b/erpnext/regional/doctype/uae_vat_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json new file mode 100644 index 0000000000..73a8169207 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.json @@ -0,0 +1,35 @@ +{ + "actions": [], + "autoname": "account", + "creation": "2020-09-28 11:30:45.472053", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "in_preview": 1, + "label": "Account", + "options": "Account" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-28 12:02:56.444007", + "modified_by": "Administrator", + "module": "Regional", + "name": "UAE VAT Account", + "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/regional/doctype/uae_vat_account/uae_vat_account.py b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.py new file mode 100644 index 0000000000..80d6b3a5f1 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_account/uae_vat_account.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 UAEVATAccount(Document): + pass diff --git a/erpnext/regional/doctype/uae_vat_setting/__init__.py b/erpnext/regional/doctype/uae_vat_setting/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py b/erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py new file mode 100644 index 0000000000..a5f151dc73 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.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 TestUAEVATSetting(unittest.TestCase): + pass diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js new file mode 100644 index 0000000000..f910e1f662 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.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('UAE VAT Setting', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json new file mode 100644 index 0000000000..e2e2ab8245 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "autoname": "field:company", + "creation": "2020-09-25 12:48:51.463265", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "uae_vat_account" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "uae_vat_account", + "fieldtype": "Table", + "label": "UAE VAT Account", + "options": "UAE VAT Account", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-09-28 12:19:11.493138", + "modified_by": "Administrator", + "module": "Regional", + "name": "UAE VAT Setting", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py new file mode 100644 index 0000000000..9549de9466 --- /dev/null +++ b/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.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 UAEVATSetting(Document): + pass diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 5242c63e63..4424d2efec 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -53,12 +53,13 @@ def get_tax_accounts(company): tax_accounts: List of Tax Accounts for the company """ tax_accounts_dict = frappe._dict() - tax_accounts_list = frappe.get_all("Account", - filters={"account_type": "Tax", "company": company}, - fields=["name"]) + tax_accounts_list = frappe.get_all("UAE VAT Account", + filters={"parent": company}, + fields=["Account"] + ) if not tax_accounts_list and not frappe.flags.in_test: - frappe.throw(_("Please create at least one Account of type Tax")) + frappe.throw(_(f'Please set Vat Accounts for Company: "{company}" in UAE VAT Setting')) for d in tax_accounts_list: for key, name in d.items(): tax_accounts_dict[name] = name From 0860c74c824e337a2e5e689d67614a2644e663b8 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 14:38:45 +0530 Subject: [PATCH 028/283] feat(UAE VAT 21): Add permissions for doctypes --- erpnext/regional/united_arab_emirates/setup.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 07543b31ea..1c947d2fb0 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -5,13 +5,14 @@ from __future__ import unicode_literals import frappe, os, json from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax def setup(company=None, patch=True): make_custom_fields() add_print_formats() add_custom_roles_for_reports() - + add_permissions() if company: create_sales_tax(company) @@ -141,4 +142,14 @@ def add_custom_roles_for_reports(): dict(role='Accounts Manager'), dict(role='Auditor') ] - )).insert() \ No newline at end of file + )).insert() + +def add_permissions(): + """Add Permissions for UAE VAT Settings and UAE VAT Account + """ + for doctype in ('UAE VAT Setting', 'UAE VAT Account'): + add_permission(doctype, 'All', 0) + for role in ('Accounts Manager', 'Accounts User', 'System Manager'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) From 587bb05a37a068962620526c47450731ee09d83f Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 14:52:29 +0530 Subject: [PATCH 029/283] feat(UAE VAT 21): Add desk page for UAE VAT 21 --- erpnext/accounts/desk_page/accounting/accounting.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 2c5231491c..6621e8e0ce 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -79,6 +79,11 @@ "hidden": 0, "label": "Profitability", "links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Value-Added Tax (VAT UAE)", + "links": "[\n {\n \"label\": \"UAE VAT Setting\",\n \"name\": \"UAE VAT Setting\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 21\",\n \"name\": \"UAE VAT 21\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -98,7 +103,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-09-03 10:37:07.865801", + "modified": "2020-09-28 13:19:27.703992", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", From 80068e16e856ebd37c782132be1d68c0c9d2e923 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 18:58:27 +0530 Subject: [PATCH 030/283] Refactor(UAE VAT 21): Replace f-strings by format --- .../regional/report/uae_vat_21/uae_vat_21.py | 135 ++++++++---------- 1 file changed, 57 insertions(+), 78 deletions(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py index d21cecd826..6cb39e0835 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.py @@ -218,9 +218,9 @@ def get_conditions(filters): String: Concatenated list of conditions to be applied to calculate the total sale """ conditions = "" - for opts in (("company", f' and company="{filters.get("company")}"'), - ("from_date", f' and posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and posting_date<="{filters.get("to_date")}"')): + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): if filters.get(opts[0]): conditions += opts[1] return conditions @@ -234,21 +234,21 @@ def get_reverse_charge_total(filters): Returns: Float: sum of the total of each Purchase invoice made """ - conditions = """ - for opts in (("company", f' and company="{filters.get("company")}"'), - ("from_date", f' and posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and posting_date<="{filters.get("to_date")}"')): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + print(""" select sum(total) from `tabPurchase Invoice` where reverse_charge = "Y" - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions)) + return frappe.db.sql(""" + select sum(total) from + `tabPurchase Invoice` + where + reverse_charge = "Y" + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_reverse_charge_tax(filters): """Returns the sum of the tax of each Purchase invoice made @@ -259,18 +259,18 @@ def get_reverse_charge_tax(filters): Returns: Float: sum of the tax of each Purchase invoice made """ - return frappe.db.sql(f""" + conditions = get_conditions_join(filters) + return frappe.db.sql(""" select sum(debit) from `tabPurchase Invoice` inner join `tabGL Entry` on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name where `tabPurchase Invoice`.reverse_charge = "Y" and `tabPurchase Invoice`.docstatus = 1 - and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} - and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); - """)[0][0] or 0 - - + and `tabGL Entry`.docstatus = 1 + and account in (select account from `tabUAE VAT Account` where parent=%(company)s) + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_conditions_join(filters): """The conditions to be used to filter data to calculate the total vat @@ -282,14 +282,13 @@ def get_conditions_join(filters): String: Concatenated list of conditions to be applied to calculate the total vat """ conditions = "" - for opts in (("company", f' and `tabPurchase Invoice`.company="{filters.get("company")}"'), - ("from_date", f' and `tabPurchase Invoice`.posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and `tabPurchase Invoice`.posting_date<="{filters.get("to_date")}"')): + for opts in (("company", " and `tabPurchase Invoice`.company=%(company)s"), + ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), + ("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s")): if filters.get(opts[0]): conditions += opts[1] return conditions - def get_reverse_charge_recoverable_total(filters): """Returns the sum of the total of each Purchase invoice made with claimable reverse charge @@ -299,23 +298,15 @@ def get_reverse_charge_recoverable_total(filters): Returns: Float: sum of the total of each Purchase invoice made with claimable reverse charge """ - conditions = """ - for opts in (("company", f' and company="{filters.get("company")}"'), - ("from_date", f' and posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and posting_date<="{filters.get("to_date")}"')): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(total) from `tabPurchase Invoice` where reverse_charge = "Y" and claimable_reverse_charge > 0 - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 - + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_reverse_charge_recoverable_tax(filters): """Returns the sum of the tax of each Purchase invoice made @@ -326,7 +317,8 @@ def get_reverse_charge_recoverable_tax(filters): Returns: Float: sum of the tax of each Purchase invoice made """ - return frappe.db.sql(f""" + conditions = get_conditions_join(filters) + return frappe.db.sql(""" select sum(debit * `tabPurchase Invoice`.claimable_reverse_charge / 100) from `tabPurchase Invoice` inner join `tabGL Entry` on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name @@ -334,10 +326,10 @@ def get_reverse_charge_recoverable_tax(filters): `tabPurchase Invoice`.reverse_charge = "Y" and `tabPurchase Invoice`.docstatus = 1 and `tabPurchase Invoice`.claimable_reverse_charge > 0 - and `tabGL Entry`.docstatus = 1 {get_conditions_join(filters)} - and account in ("{'", "'.join(get_tax_accounts(filters['company']))}"); - """)[0][0] or 0 - + and `tabGL Entry`.docstatus = 1 + and account in (select account from `tabUAE VAT Account` where parent=%(company)s) + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_total(filters): """Returns the sum of the total of each Purchase invoice made with claimable reverse charge @@ -348,22 +340,14 @@ def get_standard_rated_expenses_total(filters): Returns: Float: sum of the total of each Purchase invoice made with claimable reverse charge """ - conditions = """ - for opts in (("company", f' and company="{filters.get("company")}"'), - ("from_date", f' and posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and posting_date<="{filters.get("to_date")}"')): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(total) from `tabSales Invoice` where standard_rated_expenses > 0 - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 - + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made @@ -374,13 +358,14 @@ def get_standard_rated_expenses_tax(filters): Returns: Float: sum of the tax of each Purchase invoice made """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(standard_rated_expenses) from `tabSales Invoice` where standard_rated_expenses > 0 - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_tourist_tax_return_total(filters): """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return @@ -391,22 +376,14 @@ def get_tourist_tax_return_total(filters): Returns: Float: sum of the total of each Sales invoice with non zero tourist_tax_return """ - conditions = """ - for opts in (("company", f' and company="{filters.get("company")}"'), - ("from_date", f' and posting_date>="{filters.get("from_date")}"'), - ("to_date", f' and posting_date<="{filters.get("to_date")}"')): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(total) from `tabSales Invoice` where tourist_tax_return > 0 - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 - + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_tourist_tax_return_tax(filters): """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return @@ -417,13 +394,14 @@ def get_tourist_tax_return_tax(filters): Returns: Float: sum of the tax of each Sales invoice with non zero tourist_tax_return """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(tourist_tax_return) from `tabSales Invoice` where tourist_tax_return > 0 - and docstatus = 1 {get_conditions(filters)} ; - """)[0][0] or 0 + and docstatus = 1 {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_zero_rated_total(filters): """Returns the sum of each Sales Invoice Item Amount which is zero rated @@ -434,13 +412,13 @@ def get_zero_rated_total(filters): Returns: Float: sum of each Sales Invoice Item Amount which is zero rated """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(i.base_amount) as total from `tabSales Invoice Item` i, `tabSales Invoice` s where s.docstatus = 1 and i.parent = s.name and i.is_zero_rated = 1 - {get_conditions(filters)} ; - """)[0][0] or 0 - + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 def get_exempt_total(filters): """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt @@ -451,9 +429,10 @@ def get_exempt_total(filters): Returns: Float: sum of each Sales Invoice Item Amount which is Vat Exempt """ - return frappe.db.sql(f""" + conditions = get_conditions(filters) + return frappe.db.sql(""" select sum(i.base_amount) as total from `tabSales Invoice Item` i, `tabSales Invoice` s where s.docstatus = 1 and i.parent = s.name and i.is_exempt = 1 - {get_conditions(filters)} ; - """)[0][0] or 0 \ No newline at end of file + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 \ No newline at end of file From a5e8e449eea1266b225d1616b0b400cd51feeb01 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Mon, 28 Sep 2020 20:43:03 +0530 Subject: [PATCH 031/283] chore: Merge branch 'develop' into UAE-VAT-Format --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 29d1331b5d..85635572b1 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -103,11 +103,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", -<<<<<<< HEAD "modified": "2020-09-28 13:19:27.703992", -======= - "modified": "2020-09-09 11:45:33.766400", ->>>>>>> develop "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", From 3294f86b9235d57f9f143e7cc33cf367816d4aa1 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Wed, 30 Sep 2020 12:15:07 +0530 Subject: [PATCH 032/283] refactor: fix linting --- .../regional/report/uae_vat_21/uae_vat_21.py | 168 +++--------------- .../regional/united_arab_emirates/setup.py | 6 +- .../regional/united_arab_emirates/utils.py | 39 +--- 3 files changed, 36 insertions(+), 177 deletions(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py index 6cb39e0835..d400732f39 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import frappe -from erpnext.regional.united_arab_emirates.utils import get_tax_accounts from frappe import _ def execute(filters=None): @@ -14,11 +13,7 @@ def execute(filters=None): return columns, data, None, chart def get_columns(): - """Creates a list of dictionaries that are used to generate column headers of the data table - - Returns: - List(Dict): list of dictionaries that are used to generate column headers of the data table - """ + """Creates a list of dictionaries that are used to generate column headers of the data table.""" return [ { "fieldname": "no", @@ -47,22 +42,15 @@ def get_columns(): ] def get_data(filters = None): - """Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - List(Dict): Each dictionary is a row in the datatable - Dict: Dictionary containing chart data - """ + """Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data.""" data = [] data.append({ "no": '', - "legend": f'VAT on Sales and All Other Outputs', + "legend": 'VAT on Sales and All Other Outputs', "amount": '', "vat_amount": '' }) + total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} @@ -92,7 +80,7 @@ def get_data(filters = None): data.append( { "no": '2', - "legend": f'Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme', + "legend": 'Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme', "amount": (-1) * get_tourist_tax_return_total(filters), "vat_amount": (-1) * get_tourist_tax_return_tax(filters) } @@ -101,7 +89,7 @@ def get_data(filters = None): data.append( { "no": '3', - "legend": f'Supplies subject to the reverse charge provision', + "legend": 'Supplies subject to the reverse charge provision', "amount": get_reverse_charge_total(filters), "vat_amount": get_reverse_charge_tax(filters) } @@ -110,7 +98,7 @@ def get_data(filters = None): data.append( { "no": '4', - "legend": f'Zero Rated', + "legend": 'Zero Rated', "amount": get_zero_rated_total(filters), "vat_amount": "-" } @@ -119,7 +107,7 @@ def get_data(filters = None): data.append( { "no": '5', - "legend": f'Exempt Supplies', + "legend": 'Exempt Supplies', "amount": get_exempt_total(filters), "vat_amount": "-" } @@ -127,22 +115,24 @@ def get_data(filters = None): data.append({ "no": '', - "legend": f'VAT on Expenses and All Other Inputs', + "legend": 'VAT on Expenses and All Other Inputs', "amount": '', "vat_amount": '' }) + data.append( { "no": '9', - "legend": f'Standard Rated Expenses', + "legend": 'Standard Rated Expenses', "amount": get_standard_rated_expenses_total(filters), "vat_amount": get_standard_rated_expenses_tax(filters) } ) + data.append( { "no": '10', - "legend": f'Supplies subject to the reverse charge provision', + "legend": 'Supplies subject to the reverse charge provision', "amount": get_reverse_charge_recoverable_total(filters), "vat_amount": get_reverse_charge_recoverable_tax(filters) } @@ -152,15 +142,7 @@ def get_data(filters = None): def get_chart(emirates, amounts_by_emirate): - """Returns chart data - - Args: - emirates (List): List of Emirates - amounts_by_emirate (Dict): Vat and Tax amount by emirates with emirates as key - - Returns: - [Dict]: Chart Data - """ + """Returns chart data.""" labels = [] amount = [] vat_amount = [] @@ -186,6 +168,7 @@ def get_chart(emirates, amounts_by_emirate): return chart def get_total_emiratewise(filters): + """Returns Emiratewise Amount and Taxes.""" return frappe.db.sql(f""" select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` where docstatus = 1 {get_conditions(filters)} @@ -193,11 +176,7 @@ def get_total_emiratewise(filters): """, filters) def get_emirates(): - """Returns a List of emirates in the order that they are to be displayed - - Returns: - List(String): List of emirates in the order that they are to be displayed - """ + """Returns a List of emirates in the order that they are to be displayed.""" return [ 'Abu Dhabi', 'Dubai', @@ -209,14 +188,7 @@ def get_emirates(): ] def get_conditions(filters): - """The conditions to be used to filter data to calculate the total sale - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - String: Concatenated list of conditions to be applied to calculate the total sale - """ + """The conditions to be used to filter data to calculate the total sale.""" conditions = "" for opts in (("company", " and company=%(company)s"), ("from_date", " and posting_date>=%(from_date)s"), @@ -226,22 +198,8 @@ def get_conditions(filters): return conditions def get_reverse_charge_total(filters): - """Returns the sum of the total of each Purchase invoice made - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the total of each Purchase invoice made - """ + """Returns the sum of the total of each Purchase invoice made.""" conditions = get_conditions(filters) - print(""" - select sum(total) from - `tabPurchase Invoice` - where - reverse_charge = "Y" - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions)) return frappe.db.sql(""" select sum(total) from `tabPurchase Invoice` @@ -251,14 +209,7 @@ def get_reverse_charge_total(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_reverse_charge_tax(filters): - """Returns the sum of the tax of each Purchase invoice made - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the tax of each Purchase invoice made - """ + """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" select sum(debit) from @@ -273,14 +224,7 @@ def get_reverse_charge_tax(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_conditions_join(filters): - """The conditions to be used to filter data to calculate the total vat - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - String: Concatenated list of conditions to be applied to calculate the total vat - """ + """The conditions to be used to filter data to calculate the total vat.""" conditions = "" for opts in (("company", " and `tabPurchase Invoice`.company=%(company)s"), ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), @@ -290,14 +234,7 @@ def get_conditions_join(filters): return conditions def get_reverse_charge_recoverable_total(filters): - """Returns the sum of the total of each Purchase invoice made with claimable reverse charge - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the total of each Purchase invoice made with claimable reverse charge - """ + """Returns the sum of the total of each Purchase invoice made with claimable reverse charge.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from @@ -309,14 +246,7 @@ def get_reverse_charge_recoverable_total(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_reverse_charge_recoverable_tax(filters): - """Returns the sum of the tax of each Purchase invoice made - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the tax of each Purchase invoice made - """ + """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" select sum(debit * `tabPurchase Invoice`.claimable_reverse_charge / 100) from @@ -332,14 +262,7 @@ def get_reverse_charge_recoverable_tax(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_total(filters): - """Returns the sum of the total of each Purchase invoice made with claimable reverse charge - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the total of each Purchase invoice made with claimable reverse charge - """ + """Returns the sum of the total of each Purchase invoice made with claimable reverse charge.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from @@ -350,14 +273,7 @@ def get_standard_rated_expenses_total(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_tax(filters): - """Returns the sum of the tax of each Purchase invoice made - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the tax of each Purchase invoice made - """ + """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(standard_rated_expenses) from @@ -368,14 +284,7 @@ def get_standard_rated_expenses_tax(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_tourist_tax_return_total(filters): - """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the total of each Sales invoice with non zero tourist_tax_return - """ + """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from @@ -386,14 +295,7 @@ def get_tourist_tax_return_total(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_tourist_tax_return_tax(filters): - """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of the tax of each Sales invoice with non zero tourist_tax_return - """ + """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(tourist_tax_return) from @@ -404,14 +306,7 @@ def get_tourist_tax_return_tax(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_zero_rated_total(filters): - """Returns the sum of each Sales Invoice Item Amount which is zero rated - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of each Sales Invoice Item Amount which is zero rated - """ + """Returns the sum of each Sales Invoice Item Amount which is zero rated.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(i.base_amount) as total from @@ -421,14 +316,7 @@ def get_zero_rated_total(filters): """.format(where_conditions=conditions), filters)[0][0] or 0 def get_exempt_total(filters): - """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt - - Args: - filters (Dict, optional): Dictionary consisting of the filters selected by the user. Defaults to None. - - Returns: - Float: sum of each Sales Invoice Item Amount which is Vat Exempt - """ + """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(i.base_amount) as total from diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 1c947d2fb0..70030c736c 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -131,8 +131,7 @@ def add_print_formats(): name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """) def add_custom_roles_for_reports(): - """Add Access Control to UAE VAT 21 - """ + """Add Access Control to UAE VAT 21.""" if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 21')): frappe.get_doc(dict( doctype='Custom Role', @@ -145,8 +144,7 @@ def add_custom_roles_for_reports(): )).insert() def add_permissions(): - """Add Permissions for UAE VAT Settings and UAE VAT Account - """ + """Add Permissions for UAE VAT Settings and UAE VAT Account.""" for doctype in ('UAE VAT Setting', 'UAE VAT Account'): add_permission(doctype, 'All', 0) for role in ('Accounts Manager', 'Accounts User', 'System Manager'): diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 4424d2efec..f75efd1aae 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -31,7 +31,7 @@ def update_itemised_tax_data(doc): row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount")) def get_account_currency(account): - """Helper function to get account currency""" + """Helper function to get account currency.""" if not account: return def generator(): @@ -44,14 +44,7 @@ def get_account_currency(account): return frappe.local_cache("account_currency", account, generator) def get_tax_accounts(company): - """Get the list of tax accounts for a specific company - - Args: - company (String): Current Company set as default - - Returns: - tax_accounts: List of Tax Accounts for the company - """ + """Get the list of tax accounts for a specific company.""" tax_accounts_dict = frappe._dict() tax_accounts_list = frappe.get_all("UAE VAT Account", filters={"parent": company}, @@ -67,11 +60,7 @@ def get_tax_accounts(company): return tax_accounts_dict def update_grand_total_for_rcm(doc, method): - """If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form - - Args: - doc (Document): The document for the current Purchase Invoice - """ + """If the Reverse Charge is Applicable subtract the tax amount from the grand total and update in the form.""" country = frappe.get_cached_value('Company', doc.company, 'country') if country != 'United Arab Emirates': @@ -102,14 +91,7 @@ def update_grand_total_for_rcm(doc, method): update_totals(vat_tax, base_vat_tax, doc) def update_totals(vat_tax, base_vat_tax, doc): - """Update the grand total values in the form - - Args: - vat_tax (float): Vat Tax to be subtracted - base_vat_tax (float): Base Vat Tax to be subtracted - doc (Document): The document for the current Purchase Invoice - """ - + """Update the grand total values in the form.""" doc.base_grand_total -= base_vat_tax doc.grand_total -= vat_tax @@ -130,16 +112,7 @@ def update_totals(vat_tax, base_vat_tax, doc): doc.set_payment_schedule() def make_regional_gl_entries(gl_entries, doc): - """This method is hooked to the make_regional_gl_entries in Purchase Invoice. - It appends the region specific general ledger entries to the list of GL Entries. - - Args: - gl_entries (List): List of GL entries to be made - doc (Document): The document for the current Purchase Invoice - - Returns: - List: Updates list of GL Entries - """ + """Hooked to make_regional_gl_entries in Purchase Invoice.It appends the region specific general ledger entries to the list of GL Entries.""" country = frappe.get_cached_value('Company', doc.company, 'country') if country != 'United Arab Emirates': @@ -170,7 +143,7 @@ def make_regional_gl_entries(gl_entries, doc): return gl_entries def validate_returns(doc, method): - print("validate_returns") + """Sum of Tourist Returns and Standard Rated Expenses should be less than Total Tax.""" country = frappe.get_cached_value('Company', doc.company, 'country') if country != 'United Arab Emirates': From 10a3a338b6a98d68e3d68d090801d08e7d240891 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Wed, 30 Sep 2020 21:51:00 +0530 Subject: [PATCH 033/283] refactor: change uae vat settingplural --- .../{uae_vat_setting => uae_vat_settings}/__init__.py | 0 .../test_uae_vat_settings.py} | 2 +- .../uae_vat_settings.js} | 2 +- .../uae_vat_settings.json} | 10 +++++----- .../uae_vat_settings.py} | 2 +- erpnext/regional/united_arab_emirates/setup.py | 2 +- erpnext/regional/united_arab_emirates/utils.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename erpnext/regional/doctype/{uae_vat_setting => uae_vat_settings}/__init__.py (100%) rename erpnext/regional/doctype/{uae_vat_setting/test_uae_vat_setting.py => uae_vat_settings/test_uae_vat_settings.py} (80%) rename erpnext/regional/doctype/{uae_vat_setting/uae_vat_setting.js => uae_vat_settings/uae_vat_settings.js} (80%) rename erpnext/regional/doctype/{uae_vat_setting/uae_vat_setting.json => uae_vat_settings/uae_vat_settings.json} (84%) rename erpnext/regional/doctype/{uae_vat_setting/uae_vat_setting.py => uae_vat_settings/uae_vat_settings.py} (88%) diff --git a/erpnext/regional/doctype/uae_vat_setting/__init__.py b/erpnext/regional/doctype/uae_vat_settings/__init__.py similarity index 100% rename from erpnext/regional/doctype/uae_vat_setting/__init__.py rename to erpnext/regional/doctype/uae_vat_settings/__init__.py diff --git a/erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py similarity index 80% rename from erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py rename to erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py index a5f151dc73..b88439f9b8 100644 --- a/erpnext/regional/doctype/uae_vat_setting/test_uae_vat_setting.py +++ b/erpnext/regional/doctype/uae_vat_settings/test_uae_vat_settings.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe import unittest -class TestUAEVATSetting(unittest.TestCase): +class TestUAEVATSettings(unittest.TestCase): pass diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js similarity index 80% rename from erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js rename to erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js index f910e1f662..07a93010b5 100644 --- a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.js +++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js @@ -1,7 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('UAE VAT Setting', { +frappe.ui.form.on('UAE VAT Settings', { // refresh: function(frm) { // } diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json similarity index 84% rename from erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json rename to erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json index e2e2ab8245..ce2c1d4e14 100644 --- a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.json +++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.json @@ -7,7 +7,7 @@ "engine": "InnoDB", "field_order": [ "company", - "uae_vat_account" + "uae_vat_accounts" ], "fields": [ { @@ -20,19 +20,19 @@ "unique": 1 }, { - "fieldname": "uae_vat_account", + "fieldname": "uae_vat_accounts", "fieldtype": "Table", - "label": "UAE VAT Account", + "label": "UAE VAT Accounts", "options": "UAE VAT Account", "reqd": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-09-28 12:19:11.493138", + "modified": "2020-09-30 20:08:18.764798", "modified_by": "Administrator", "module": "Regional", - "name": "UAE VAT Setting", + "name": "UAE VAT Settings", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py similarity index 88% rename from erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py rename to erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py index 9549de9466..20dc604510 100644 --- a/erpnext/regional/doctype/uae_vat_setting/uae_vat_setting.py +++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class UAEVATSetting(Document): +class UAEVATSettings(Document): pass diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 70030c736c..0de42120c4 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -145,7 +145,7 @@ def add_custom_roles_for_reports(): def add_permissions(): """Add Permissions for UAE VAT Settings and UAE VAT Account.""" - for doctype in ('UAE VAT Setting', 'UAE VAT Account'): + for doctype in ('UAE VAT Settings', 'UAE VAT Account'): add_permission(doctype, 'All', 0) for role in ('Accounts Manager', 'Accounts User', 'System Manager'): add_permission(doctype, role, 0) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index f75efd1aae..5a8c3c47fb 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -52,7 +52,7 @@ def get_tax_accounts(company): ) if not tax_accounts_list and not frappe.flags.in_test: - frappe.throw(_(f'Please set Vat Accounts for Company: "{company}" in UAE VAT Setting')) + frappe.throw(_(f'Please set Vat Accounts for Company: "{company}" in UAE VAT Settings')) for d in tax_accounts_list: for key, name in d.items(): tax_accounts_dict[name] = name From 8cc6f2b38881f1bcd53cf558a362486e564648f1 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 1 Oct 2020 12:44:39 +0530 Subject: [PATCH 034/283] feat(UAE VAT 21): add fixed width to columns --- .../report/uae_vat_21/uae_vat_21.html | 31 ++++++---- .../regional/report/uae_vat_21/uae_vat_21.js | 1 - .../regional/report/uae_vat_21/uae_vat_21.py | 57 +++++++++++-------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.html b/erpnext/regional/report/uae_vat_21/uae_vat_21.html index aa6a255932..d9b9968d90 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.html +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.html @@ -1,21 +1,27 @@ {% var report_columns = report.get_columns_for_print(); report_columns = report_columns.filter(col => !col.hidden); - - if (report_columns.length > 8) { - frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application.")); - } %} +

{%= __(report.report_name) %}

-

{%= __("VAT on Sales and All Other Outputs") %}

+

{%= __("VAT on Sales and All Other Outputs") %}

- {% for (let i=0; i{%= report_columns[i].label %} + + + + {% for (let i=2; i{%= report_columns[i].label %} {% } %} @@ -38,17 +44,20 @@
{%= report_columns[0].label %}{%= report_columns[1].label %}
-

{%= __("VAT on Expenses and All Other Inputs") %}

+

{%= __("VAT on Expenses and All Other Inputs") %}

- {% for (let i=0; i{%= report_columns[i].label %} + + + + {% for (let i=2; i{%= report_columns[i].label %} {% } %} - {% for (let j=13; j Date: Thu, 1 Oct 2020 12:59:39 +0530 Subject: [PATCH 035/283] feat(UAE VAT 21): Add currency formatting --- .../regional/report/uae_vat_21/uae_vat_21.py | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py index 211bf16734..2bded71ec2 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.py @@ -60,8 +60,10 @@ def get_data(filters = None): emirate, amount, vat= d amounts_by_emirate[emirate] = { "legend": emirate, - "amount": amount, - "vat_amount": vat + "raw_amount": amount, + "raw_vat_amount": vat, + "amount": frappe.format(amount, 'Currency'), + "vat_amount": frappe.format(vat, 'Currency'), } for d, emirate in enumerate(emirates, 97): @@ -74,8 +76,8 @@ def get_data(filters = None): { "no": _(f'1{chr(d)}'), "legend": _(f'Standard rated supplies in {emirate}'), - "amount": 0, - "vat_amount": 0 + "amount": frappe.format(0, 'Currency'), + "vat_amount": frappe.format(0, 'Currency') } ) @@ -83,8 +85,8 @@ def get_data(filters = None): { "no": _('2'), "legend": _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), - "amount": (-1) * get_tourist_tax_return_total(filters), - "vat_amount": (-1) * get_tourist_tax_return_tax(filters) + "amount": frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'), + "vat_amount": frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency') } ) @@ -92,8 +94,8 @@ def get_data(filters = None): { "no": _('3'), "legend": _('Supplies subject to the reverse charge provision'), - "amount": get_reverse_charge_total(filters), - "vat_amount": get_reverse_charge_tax(filters) + "amount": frappe.format(get_reverse_charge_total(filters), 'Currency'), + "vat_amount": frappe.format(get_reverse_charge_tax(filters), 'Currency') } ) @@ -101,7 +103,7 @@ def get_data(filters = None): { "no": _('4'), "legend": _('Zero Rated'), - "amount": get_zero_rated_total(filters), + "amount": frappe.format(get_zero_rated_total(filters), 'Currency'), "vat_amount": "-" } ) @@ -110,7 +112,7 @@ def get_data(filters = None): { "no": _('5'), "legend": _('Exempt Supplies'), - "amount": get_exempt_total(filters), + "amount": frappe.format(get_exempt_total(filters), 'Currency'), "vat_amount": "-" } ) @@ -133,8 +135,8 @@ def get_data(filters = None): { "no": '9', "legend": _('Standard Rated Expenses'), - "amount": get_standard_rated_expenses_total(filters), - "vat_amount": get_standard_rated_expenses_tax(filters) + "amount": frappe.format(get_standard_rated_expenses_total(filters), 'Currency'), + "vat_amount": frappe.format(get_standard_rated_expenses_tax(filters), 'Currency') } ) @@ -142,8 +144,8 @@ def get_data(filters = None): { "no": _('10'), "legend": _('Supplies subject to the reverse charge provision'), - "amount": get_reverse_charge_recoverable_total(filters), - "vat_amount": get_reverse_charge_recoverable_tax(filters) + "amount": frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'), + "vat_amount": frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency') } ) @@ -157,8 +159,8 @@ def get_chart(emirates, amounts_by_emirate): vat_amount = [] for d in emirates: if d in amounts_by_emirate: - amount.append(amounts_by_emirate[d]["amount"]) - vat_amount.append(amounts_by_emirate[d]["vat_amount"]) + amount.append(amounts_by_emirate[d]["raw_amount"]) + vat_amount.append(amounts_by_emirate[d]["raw_vat_amount"]) labels.append(d) datasets = [] From 4be5b5c891066e469c1d4458e04368e218d8ccd4 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 8 Oct 2020 19:08:27 +0530 Subject: [PATCH 036/283] fix: Handle missing Account and Item in Opening Invoice Creation Tool --- .../opening_invoice_creation_tool.py | 3 ++- .../doctype/purchase_invoice/purchase_invoice.py | 5 +++++ .../doctype/sales_invoice/sales_invoice.py | 5 +++++ erpnext/controllers/accounts_controller.py | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index a53417eedf..3653a88167 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -181,7 +181,8 @@ class OpeningInvoiceCreationTool(Document): "due_date": row.due_date, "posting_date": row.posting_date, frappe.scrub(party_type): row.party, - "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice" + "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice", + "update_stock": 0 }) accounting_dimension = get_accounting_dimensions() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 079f599706..3d67a8dc35 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -147,6 +147,11 @@ class PurchaseInvoice(BuyingController): throw(_("Conversion rate cannot be 0 or 1")) def validate_credit_to_acc(self): + if not self.credit_to: + self.credit_to = get_party_account("Supplier", self.supplier, self.company) + if not self.credit_to: + self.raise_missing_debit_credit_account_error("Supplier", self.supplier) + account = frappe.db.get_value("Account", self.credit_to, ["account_type", "report_type", "account_currency"], as_dict=True) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 92e49d59da..c280184d62 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -472,6 +472,11 @@ class SalesInvoice(SellingController): return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0] def validate_debit_to_acc(self): + if not self.debit_to: + self.debit_to = get_party_account("Customer", self.customer, self.company) + if not self.debit_to: + self.raise_missing_debit_credit_account_error("Customer", self.customer) + account = frappe.get_cached_value("Account", self.debit_to, ["account_type", "report_type", "account_currency"], as_dict=True) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bb288c5551..7163e02122 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -720,6 +720,21 @@ class AccountsController(TransactionBase): return self._abbr + def raise_missing_debit_credit_account_error(self, party_type, party): + """Raise an error if debit to/credit to account does not exist""" + db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To") + rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable" + + link_to_party = frappe.utils.get_link_to_form(party_type, party) + link_to_company = frappe.utils.get_link_to_form("Company", self.company) + + message = _("{0} Account not found against Customer {1}.").format(db_or_cr, frappe.bold(party) or '') + message += "
" + _("Please set one of the following:") + "
" + message += "
  • " + _("'Account' in the Accounting section of Customer {0}").format(link_to_party) + "
  • " + message += "
  • " + _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company) + "
" + + frappe.throw(message, title=_("Account Missing")) + def validate_party(self): party_type, party = self.get_party() validate_party_frozen_disabled(party_type, party) From ac451b3a5435b2927f1a60f3e0901a45c3fceb07 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Sun, 11 Oct 2020 01:40:22 +0530 Subject: [PATCH 037/283] feat(UAE VAT 21): Move standard rated expense from Sales invoice to Purchase invoice --- erpnext/hooks.py | 4 +--- erpnext/regional/report/uae_vat_21/uae_vat_21.py | 4 ++-- erpnext/regional/united_arab_emirates/setup.py | 7 ++++--- erpnext/regional/united_arab_emirates/utils.py | 10 ++++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a0242c175c..8d08c90047 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -242,15 +242,13 @@ doc_events = { "erpnext.regional.italy.utils.sales_invoice_on_cancel", "erpnext.erpnext_integrations.taxjar_integration.delete_transaction" ], - "validate": [ - "erpnext.regional.united_arab_emirates.utils.validate_returns", - ], "on_trash": "erpnext.regional.check_deletion_permission" }, "Purchase Invoice": { "validate": [ "erpnext.regional.india.utils.update_grand_total_for_rcm", "erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm", + "erpnext.regional.united_arab_emirates.utils.validate_returns" ] }, "Payment Entry": { diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_21/uae_vat_21.py index 2bded71ec2..227c2fb5a1 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_21/uae_vat_21.py @@ -277,7 +277,7 @@ def get_standard_rated_expenses_total(filters): conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from - `tabSales Invoice` + `tabPurchase Invoice` where standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; @@ -288,7 +288,7 @@ def get_standard_rated_expenses_tax(filters): conditions = get_conditions(filters) return frappe.db.sql(""" select sum(standard_rated_expenses) from - `tabSales Invoice` + `tabPurchase Invoice` where standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 0de42120c4..e48b9a9ce1 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -38,8 +38,11 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', fetch_from='supplier.supplier_name_in_arabic', print_hide=1), + dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', + insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0', + depends_on="eval:doc.reverse_charge=='N'",), dict(fieldname='reverse_charge', label='Reverse Charge Applicable', - fieldtype='Select', insert_after='permit_no', print_hide=1, + fieldtype='Select', insert_after='standard_rated_expenses', print_hide=1, options='Y\nN', default='N'), dict(fieldname='claimable_reverse_charge', label='Claimable Reverse Charge (Percentage)', insert_after='reverse_charge', fieldtype='Percent', print_hide=1, @@ -57,8 +60,6 @@ def make_custom_fields(): fieldtype='Read Only', fetch_from='customer_address.emirates'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), - dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', - insert_after='tourist_tax_return', fieldtype='Currency', print_hide=1, default='0'), ] invoice_item_fields = [ diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 5a8c3c47fb..dbe283f226 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -143,11 +143,13 @@ def make_regional_gl_entries(gl_entries, doc): return gl_entries def validate_returns(doc, method): - """Sum of Tourist Returns and Standard Rated Expenses should be less than Total Tax.""" + """Standard Rated expenses should not be set when Reverse Charge Applicable is set.""" country = frappe.get_cached_value('Company', doc.company, 'country') - + print("-"*50) + print(doc.reverse_charge) + print(flt(doc.standard_rated_expenses)) if country != 'United Arab Emirates': return - if flt(doc.tourist_tax_return) + flt(doc.standard_rated_expenses) > flt(doc.total_taxes_and_charges): - frappe.throw(_("The Total Returns(Tax Refund provided to Tourists (AED) + Standard Rated Expenses (AED)) should be less than the Total Taxes and Charges (Company Currency)")) \ No newline at end of file + if doc.reverse_charge == 'Y' and flt(doc.standard_rated_expenses) != 0: + frappe.throw(_("Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file From a322f0d0b3163192b372a1b407468f12c9474334 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Mon, 12 Oct 2020 11:38:55 +0800 Subject: [PATCH 038/283] chore: Fix error message grammar --- erpnext/stock/doctype/item_price/item_price.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 8e39eb5037..56efa09486 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -23,7 +23,7 @@ class ItemPrice(Document): def validate_item(self): if not frappe.db.exists("Item", self.item_code): - frappe.throw(_("Item {0} not found").format(self.item_code)) + frappe.throw(_("Item {0} not found.").format(self.item_code)) def validate_dates(self): if self.valid_from and self.valid_upto: @@ -38,8 +38,7 @@ class ItemPrice(Document): if not price_list_details: link = frappe.utils.get_link_to_form('Price List', self.price_list) - frappe.throw("The price list {0} does not exists or disabled". - format(link)) + frappe.throw("The price list {0} does not exist or is disabled".format(link)) self.buying, self.selling, self.currency = price_list_details @@ -69,7 +68,7 @@ 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 From 39bed3a65e2557d15d2275178120de941e66c536 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Mon, 12 Oct 2020 11:42:05 +0800 Subject: [PATCH 039/283] fix: Add null or empty checking to validation This commit adds null or empty checking in the **Item Price** DocType's `check_duplicates()`. This was done to fix a bug where some prices are shown to be duplciates even though they aren't because they don't have values in some fields. --- .../stock/doctype/item_price/item_price.py | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 56efa09486..f88b05cd20 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -48,20 +48,52 @@ class ItemPrice(Document): self.item_code,["item_name", "description"]) def check_duplicates(self): - conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" + conditions = """ + where + item_code = %(item_code)s + and price_list = %(price_list)s + and name != %(name)s + """ - for field in ['uom', 'valid_from', - 'valid_upto', 'packing_unit', 'customer', 'supplier']: + for field in [ + "uom", + "valid_from", + "valid_upto", + "packing_unit", + "customer", + "supplier", + ]: if self.get(field): - conditions += " and {0} = %({1})s".format(field, field) + conditions += " and {0} = %({0})s ".format(field) + else: + conditions += """ + and ( + isnull({0}) + or {0} = '' + ) + """.format( + field + ) - price_list_rate = frappe.db.sql(""" - SELECT price_list_rate - FROM `tabItem Price` - {conditions} """.format(conditions=conditions), self.as_dict()) + price_list_rate = frappe.db.sql( + """ + select + price_list_rate + from + `tabItem Price` + {conditions} + """.format( + conditions=conditions + ), + self.as_dict(), + ) - if price_list_rate : - frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem) + if price_list_rate: + frappe.throw(_(""" + Item Price appears multiple times based on + Price List, Supplier/Customer, Currency, Item, UOM, Qty, + and Dates. + """), ItemPriceDuplicateItem,) def before_save(self): if self.selling: From 58389ebd402d461903fccb522127bdc1adbbb40e Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Mon, 12 Oct 2020 15:20:11 +0800 Subject: [PATCH 040/283] style: Collapse whitespace --- .../stock/doctype/item_price/item_price.py | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index f88b05cd20..f05de22d01 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -66,21 +66,12 @@ class ItemPrice(Document): if self.get(field): conditions += " and {0} = %({0})s ".format(field) else: - conditions += """ - and ( - isnull({0}) - or {0} = '' - ) - """.format( - field - ) + conditions += "and (isnull({0}) or {0} = '')".format(field) price_list_rate = frappe.db.sql( """ - select - price_list_rate - from - `tabItem Price` + select price_list_rate + from `tabItem Price` {conditions} """.format( conditions=conditions @@ -89,11 +80,7 @@ class ItemPrice(Document): ) if price_list_rate: - frappe.throw(_(""" - Item Price appears multiple times based on - Price List, Supplier/Customer, Currency, Item, UOM, Qty, - and Dates. - """), ItemPriceDuplicateItem,) + frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,) def before_save(self): if self.selling: From f6bcd5c2b230375f4014e8270a41027da9b9c6b7 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Mon, 12 Oct 2020 16:57:33 +0800 Subject: [PATCH 041/283] test: Add test case for duplicate checking --- .../doctype/item_price/test_item_price.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py index 702acc38fe..f3d406eeca 100644 --- a/erpnext/stock/doctype/item_price/test_item_price.py +++ b/erpnext/stock/doctype/item_price/test_item_price.py @@ -138,4 +138,23 @@ class TestItemPrice(unittest.TestCase): # Valid price list must already exist self.assertRaises(frappe.ValidationError, doc.save) + def test_empty_duplicate_validation(self): + # Check if none/empty values are not compared during insert validation + doc = frappe.copy_doc(test_records[2]) + doc.customer = None + doc.price_list_rate = 21 + doc.insert() + + args = { + "price_list": doc.price_list, + "uom": "_Test UOM", + "transaction_date": '2017-04-18', + "qty": 7 + } + + price = get_price_list_rate_for(args, doc.item_code) + frappe.db.rollback() + + self.assertEqual(price, 21) + test_records = frappe.get_test_records('Item Price') From dfc3993cecdd7f0e7d9cbd80734fbf8c6b8021f2 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 14 Oct 2020 12:21:36 +0530 Subject: [PATCH 042/283] chore(UAE VAT 21): rename report --- erpnext/accounts/desk_page/accounting/accounting.json | 2 +- .../report/{uae_vat_21 => uae_vat_201}/__init__.py | 0 .../uae_vat_21.html => uae_vat_201/uae_vat_201.html} | 0 .../uae_vat_21.js => uae_vat_201/uae_vat_201.js} | 2 +- .../uae_vat_21.json => uae_vat_201/uae_vat_201.json} | 4 ++-- .../uae_vat_21.py => uae_vat_201/uae_vat_201.py} | 6 +++--- erpnext/regional/united_arab_emirates/setup.py | 10 +++++----- erpnext/regional/united_arab_emirates/utils.py | 8 ++------ 8 files changed, 14 insertions(+), 18 deletions(-) rename erpnext/regional/report/{uae_vat_21 => uae_vat_201}/__init__.py (100%) rename erpnext/regional/report/{uae_vat_21/uae_vat_21.html => uae_vat_201/uae_vat_201.html} (100%) rename erpnext/regional/report/{uae_vat_21/uae_vat_21.js => uae_vat_201/uae_vat_201.js} (96%) rename erpnext/regional/report/{uae_vat_21/uae_vat_21.json => uae_vat_201/uae_vat_201.json} (88%) rename erpnext/regional/report/{uae_vat_21/uae_vat_21.py => uae_vat_201/uae_vat_201.py} (98%) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 214e31f21f..7d25b27e77 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -83,7 +83,7 @@ { "hidden": 0, "label": "Value-Added Tax (VAT UAE)", - "links": "[\n {\n \"label\": \"UAE VAT Setting\",\n \"name\": \"UAE VAT Setting\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 21\",\n \"name\": \"UAE VAT 21\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"label\": \"UAE VAT Setting\",\n \"name\": \"UAE VAT Setting\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", diff --git a/erpnext/regional/report/uae_vat_21/__init__.py b/erpnext/regional/report/uae_vat_201/__init__.py similarity index 100% rename from erpnext/regional/report/uae_vat_21/__init__.py rename to erpnext/regional/report/uae_vat_201/__init__.py diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.html b/erpnext/regional/report/uae_vat_201/uae_vat_201.html similarity index 100% rename from erpnext/regional/report/uae_vat_21/uae_vat_21.html rename to erpnext/regional/report/uae_vat_201/uae_vat_201.html diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js similarity index 96% rename from erpnext/regional/report/uae_vat_21/uae_vat_21.js rename to erpnext/regional/report/uae_vat_201/uae_vat_201.js index 5fc5449655..5957424770 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.js +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js @@ -2,7 +2,7 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports["UAE VAT 21"] = { +frappe.query_reports["UAE VAT 201"] = { "filters": [ { "fieldname": "company", diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.json b/erpnext/regional/report/uae_vat_201/uae_vat_201.json similarity index 88% rename from erpnext/regional/report/uae_vat_21/uae_vat_21.json rename to erpnext/regional/report/uae_vat_201/uae_vat_201.json index 421990cc4a..8a88bcd3e2 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.json +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.json @@ -12,11 +12,11 @@ "modified": "2020-09-10 08:51:02.298482", "modified_by": "Administrator", "module": "Regional", - "name": "UAE VAT 21", + "name": "UAE VAT 201", "owner": "Administrator", "prepared_report": 0, "ref_doctype": "GL Entry", - "report_name": "UAE VAT 21", + "report_name": "UAE VAT 201", "report_type": "Script Report", "roles": [] } \ No newline at end of file diff --git a/erpnext/regional/report/uae_vat_21/uae_vat_21.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py similarity index 98% rename from erpnext/regional/report/uae_vat_21/uae_vat_21.py rename to erpnext/regional/report/uae_vat_201/uae_vat_201.py index 227c2fb5a1..bcd31351ea 100644 --- a/erpnext/regional/report/uae_vat_21/uae_vat_21.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -279,7 +279,7 @@ def get_standard_rated_expenses_total(filters): select sum(total) from `tabPurchase Invoice` where - standard_rated_expenses > 0 + claimable_standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 @@ -287,10 +287,10 @@ def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions(filters) return frappe.db.sql(""" - select sum(standard_rated_expenses) from + select sum(claimable_standard_rated_expenses) from `tabPurchase Invoice` where - standard_rated_expenses > 0 + claimable_standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index e48b9a9ce1..a0e7620919 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -38,11 +38,11 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', fetch_from='supplier.supplier_name_in_arabic', print_hide=1), - dict(fieldname='standard_rated_expenses', label='Standard Rated Expenses (AED)', + dict(fieldname='claimable_standard_rated_expenses', label='Claimable Standard Rated Expenses (AED)', insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0', depends_on="eval:doc.reverse_charge=='N'",), dict(fieldname='reverse_charge', label='Reverse Charge Applicable', - fieldtype='Select', insert_after='standard_rated_expenses', print_hide=1, + fieldtype='Select', insert_after='claimable_standard_rated_expenses', print_hide=1, options='Y\nN', default='N'), dict(fieldname='claimable_reverse_charge', label='Claimable Reverse Charge (Percentage)', insert_after='reverse_charge', fieldtype='Percent', print_hide=1, @@ -132,11 +132,11 @@ def add_print_formats(): name in('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice') """) def add_custom_roles_for_reports(): - """Add Access Control to UAE VAT 21.""" - if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 21')): + """Add Access Control to UAE VAT 201.""" + if not frappe.db.get_value('Custom Role', dict(report='UAE VAT 201')): frappe.get_doc(dict( doctype='Custom Role', - report='UAE VAT 21', + report='UAE VAT 201', roles= [ dict(role='Accounts User'), dict(role='Accounts Manager'), diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index dbe283f226..37501a2638 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -145,11 +145,7 @@ def make_regional_gl_entries(gl_entries, doc): def validate_returns(doc, method): """Standard Rated expenses should not be set when Reverse Charge Applicable is set.""" country = frappe.get_cached_value('Company', doc.company, 'country') - print("-"*50) - print(doc.reverse_charge) - print(flt(doc.standard_rated_expenses)) if country != 'United Arab Emirates': return - - if doc.reverse_charge == 'Y' and flt(doc.standard_rated_expenses) != 0: - frappe.throw(_("Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file + if doc.reverse_charge == 'Y' and flt(doc.claimable_standard_rated_expenses) != 0: + frappe.throw(_("Claimable Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file From b9dc13294d7233eebe37783394550a3bdbe0a334 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 14 Oct 2020 16:56:26 +0530 Subject: [PATCH 043/283] chore(UAE VAT 201) solve translations issues --- .../report/uae_vat_201/uae_vat_201.py | 24 +++++++++---------- .../regional/united_arab_emirates/utils.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index bcd31351ea..a2518c38c6 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -68,14 +68,14 @@ def get_data(filters = None): for d, emirate in enumerate(emirates, 97): if emirate in amounts_by_emirate: - amounts_by_emirate[emirate]["no"] = _(f'1{chr(d)}') - amounts_by_emirate[emirate]["legend"] = _(f'Standard rated supplies in {emirate}') + amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) + amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) data.append(amounts_by_emirate[emirate]) else: data.append( { - "no": _(f'1{chr(d)}'), - "legend": _(f'Standard rated supplies in {emirate}'), + "no": _('1{0}').format(chr(d)), + "legend": _('Standard rated supplies in {0}').format(emirate), "amount": frappe.format(0, 'Currency'), "vat_amount": frappe.format(0, 'Currency') } @@ -83,7 +83,7 @@ def get_data(filters = None): data.append( { - "no": _('2'), + "no": '2', "legend": _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), "amount": frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'), "vat_amount": frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency') @@ -92,7 +92,7 @@ def get_data(filters = None): data.append( { - "no": _('3'), + "no": '3', "legend": _('Supplies subject to the reverse charge provision'), "amount": frappe.format(get_reverse_charge_total(filters), 'Currency'), "vat_amount": frappe.format(get_reverse_charge_tax(filters), 'Currency') @@ -101,7 +101,7 @@ def get_data(filters = None): data.append( { - "no": _('4'), + "no": '4', "legend": _('Zero Rated'), "amount": frappe.format(get_zero_rated_total(filters), 'Currency'), "vat_amount": "-" @@ -110,7 +110,7 @@ def get_data(filters = None): data.append( { - "no": _('5'), + "no": '5', "legend": _('Exempt Supplies'), "amount": frappe.format(get_exempt_total(filters), 'Currency'), "vat_amount": "-" @@ -118,14 +118,14 @@ def get_data(filters = None): ) data.append({ - "no": _(''), - "legend": _(''), + "no": '', + "legend": '', "amount": '', "vat_amount": '' }) data.append({ - "no": _(''), + "no": '', "legend": _('VAT on Expenses and All Other Inputs'), "amount": '', "vat_amount": '' @@ -142,7 +142,7 @@ def get_data(filters = None): data.append( { - "no": _('10'), + "no": '10', "legend": _('Supplies subject to the reverse charge provision'), "amount": frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'), "vat_amount": frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency') diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 37501a2638..847bda8271 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -52,7 +52,7 @@ def get_tax_accounts(company): ) if not tax_accounts_list and not frappe.flags.in_test: - frappe.throw(_(f'Please set Vat Accounts for Company: "{company}" in UAE VAT Settings')) + frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company)) for d in tax_accounts_list: for key, name in d.items(): tax_accounts_dict[name] = name From 359778e2357997aaeac126c37bdcb34e8efa7ed3 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 16 Oct 2020 16:47:23 +0530 Subject: [PATCH 044/283] chore: Code cleanup, reduce redundancy --- .../request_for_quotation.js | 2 +- .../supplier_quotation/supplier_quotation.js | 2 +- erpnext/public/js/controllers/buying.js | 78 +++++++++---------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 660af96505..1ebd21a17b 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -326,7 +326,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e d.show(); }, __("Get items from")); - //Link Material Requests + // Link Material Requests this.frm.add_custom_button(__('Link to Material Requests'), function() { erpnext.buying.link_to_mrs(me.frm); diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index c146f13dfe..934d71c3b3 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -55,7 +55,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext }) }, __("Get items from")); - //Link Material Requests + // Link Material Requests this.frm.add_custom_button(__('Link to Material Requests'), function() { erpnext.buying.link_to_mrs(me.frm); diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 62da7f5c5b..8cae7a5b3d 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -363,54 +363,54 @@ erpnext.buying.link_to_mrs = function(frm) { frappe.call({ method: "erpnext.buying.utils.get_linked_material_requests", args:{ - items: frm.doc.items.map((item) => {return item.item_code;}) + items: frm.doc.items.map((item) => item.item_code) }, callback: function(r) { - if(!r.message || r.message.length == 0) { - frappe.throw({message: __("No pending Material Requests found to link for the given items."), title: __("Note")}); + if (!r.message || r.message.length == 0) { + frappe.throw({ + message: __("No pending Material Requests found to link for the given items."), + title: __("Note") + }); } - else { - var i = 0; - var item_length = frm.doc.items.length; - while (i < item_length) { - var qty = frm.doc.items[i].qty; - (r.message[0] || []).forEach(function(d) { - if (d.qty > 0 && qty > 0 && frm.doc.items[i].item_code == d.item_code && !frm.doc.items[i].material_request_item) + + var item_length = frm.doc.items.length; + for (let item of frm.doc.items) { + var qty = item.qty; + (r.message[0] || []).forEach(function(d) { + if (d.qty > 0 && qty > 0 && item.item_code == d.item_code && !item.material_request_item) + { + item.material_request = d.mr_name; + item.material_request_item = d.mr_item; + var my_qty = Math.min(qty, d.qty); + qty = qty - my_qty; + d.qty = d.qty - my_qty; + item.stock_qty = my_qty*item.conversion_factor; + item.qty = my_qty; + + frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + item.idx + ")"); + if (qty > 0) { - frm.doc.items[i].material_request = d.mr_name; - frm.doc.items[i].material_request_item = d.mr_item; - var my_qty = Math.min(qty, d.qty); - qty = qty - my_qty; - d.qty = d.qty - my_qty; - frm.doc.items[i].stock_qty = my_qty*frm.doc.items[i].conversion_factor; - frm.doc.items[i].qty = my_qty; + frappe.msgprint("Splitting " + qty + " units of " + d.item_code); + var newrow = frappe.model.add_child(frm.doc, item.doctype, "items"); + item_length++; - frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + frm.doc.items[i].idx + ")"); - if (qty > 0) + for (var key in item) { - frappe.msgprint("Splitting " + qty + " units of " + d.item_code); - var newrow = frappe.model.add_child(frm.doc, frm.doc.items[i].doctype, "items"); - item_length++; - - for (var key in frm.doc.items[i]) - { - newrow[key] = frm.doc.items[i][key]; - } - - newrow.idx = item_length; - newrow["stock_qty"] = newrow.conversion_factor*qty; - newrow["qty"] = qty; - - newrow["material_request"] = ""; - newrow["material_request_item"] = ""; - + newrow[key] = item[key]; } + + newrow.idx = item_length; + newrow["stock_qty"] = newrow.conversion_factor*qty; + newrow["qty"] = qty; + + newrow["material_request"] = ""; + newrow["material_request_item"] = ""; + } - }); - i++; - } - refresh_field("items"); + } + }); } + refresh_field("items"); } }); } From c70cc0d95080d3337b4613c56983e5fbaad47426 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 29 Sep 2020 21:37:08 +0530 Subject: [PATCH 045/283] fix: tds calculation, skip invoices with "Apply Tax Withholding Amount" has disabled --- .../tax_withholding_category.py | 6 ++--- .../test_tax_withholding_category.py | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 4 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 8b5e68b359..32ad4cb03a 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -140,9 +140,9 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai else: tds_amount = _get_tds(net_total, tax_details.rate) else: - supplier_credit_amount = frappe.get_all('Purchase Invoice Item', - fields = ['sum(net_amount)'], - filters = {'parent': ('in', vouchers), 'docstatus': 1}, as_list=1) + supplier_credit_amount = frappe.get_all('Purchase Invoice', + fields = ['sum(net_total)'], + filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1) supplier_credit_amount = (supplier_credit_amount[0][0] if supplier_credit_amount and supplier_credit_amount[0][0] else 0) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index b1468999fc..a0b0cbb995 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -101,6 +101,29 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in invoices: d.cancel() + def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS") + pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi.submit() + invoices.append(pi) + + # TDS not applied + pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True) + pi.submit() + invoices.append(pi) + + pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes_and_charges_deducted, 2000) + self.assertEqual(pi.grand_total, 8000) + + # delete invoices to avoid clashing + for d in invoices: + d.cancel() + def create_purchase_invoice(**args): # return sales invoice doc object item = frappe.get_doc('Item', {'item_name': 'TDS Item'}) @@ -109,7 +132,7 @@ def create_purchase_invoice(**args): pi = frappe.get_doc({ "doctype": "Purchase Invoice", "posting_date": today(), - "apply_tds": 1, + "apply_tds": 0 if args.do_not_apply_tds else 1, "supplier": args.supplier, "company": '_Test Company', "taxes_and_charges": "", From 32eb9a78930d2771f68ee25ad1930bda5929545f Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 19 Oct 2020 17:58:50 +0530 Subject: [PATCH 046/283] feat(UAE VAT 201): claimable to recoverable --- erpnext/regional/united_arab_emirates/setup.py | 10 +++++----- erpnext/regional/united_arab_emirates/utils.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index a0e7620919..efa39bd3c5 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -38,13 +38,13 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', fetch_from='supplier.supplier_name_in_arabic', print_hide=1), - dict(fieldname='claimable_standard_rated_expenses', label='Claimable Standard Rated Expenses (AED)', + dict(fieldname='recoverable_standard_rated_expenses', label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0', depends_on="eval:doc.reverse_charge=='N'",), dict(fieldname='reverse_charge', label='Reverse Charge Applicable', - fieldtype='Select', insert_after='claimable_standard_rated_expenses', print_hide=1, + fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1, options='Y\nN', default='N'), - dict(fieldname='claimable_reverse_charge', label='Claimable Reverse Charge (Percentage)', + dict(fieldname='recoverable_reverse_charge', label='Recoverable Reverse Charge (Percentage)', insert_after='reverse_charge', fieldtype='Percent', print_hide=1, depends_on="eval:doc.reverse_charge=='Y'", default='100.000'), ] @@ -57,7 +57,7 @@ def make_custom_fields(): fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='emirate', label='Emirate', insert_after='customer_address', - fieldtype='Read Only', fetch_from='customer_address.emirates'), + fieldtype='Read Only', fetch_from='customer_address.emirate'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), ] @@ -102,7 +102,7 @@ def make_custom_fields(): fieldtype='Data', insert_after='supplier_name'), ], 'Address': [ - dict(fieldname='emirates', label='Emirates', fieldtype='Select', insert_after='state', + dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', options='Abu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 847bda8271..7edc2b6720 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -147,5 +147,5 @@ def validate_returns(doc, method): country = frappe.get_cached_value('Company', doc.company, 'country') if country != 'United Arab Emirates': return - if doc.reverse_charge == 'Y' and flt(doc.claimable_standard_rated_expenses) != 0: - frappe.throw(_("Claimable Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file + if doc.reverse_charge == 'Y' and flt(doc.recoverable_standard_rated_expenses) != 0: + frappe.throw(_("Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file From c1b80047571a579c34899ad4e458c28e89bf4c31 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 19 Oct 2020 20:47:56 +0530 Subject: [PATCH 047/283] feat(UAE VAT 21): claimable to recoverable --- .../regional/report/uae_vat_201/uae_vat_201.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index a2518c38c6..b5ff8389e3 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -245,14 +245,14 @@ def get_conditions_join(filters): return conditions def get_reverse_charge_recoverable_total(filters): - """Returns the sum of the total of each Purchase invoice made with claimable reverse charge.""" + """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from `tabPurchase Invoice` where reverse_charge = "Y" - and claimable_reverse_charge > 0 + and recoverable_reverse_charge > 0 and docstatus = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 @@ -260,26 +260,26 @@ def get_reverse_charge_recoverable_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" - select sum(debit * `tabPurchase Invoice`.claimable_reverse_charge / 100) from + select sum(debit * `tabPurchase Invoice`.recoverable_reverse_charge / 100) from `tabPurchase Invoice` inner join `tabGL Entry` on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name where `tabPurchase Invoice`.reverse_charge = "Y" and `tabPurchase Invoice`.docstatus = 1 - and `tabPurchase Invoice`.claimable_reverse_charge > 0 + and `tabPurchase Invoice`.recoverable_reverse_charge > 0 and `tabGL Entry`.docstatus = 1 and account in (select account from `tabUAE VAT Account` where parent=%(company)s) {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_total(filters): - """Returns the sum of the total of each Purchase invoice made with claimable reverse charge.""" + """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" conditions = get_conditions(filters) return frappe.db.sql(""" select sum(total) from `tabPurchase Invoice` where - claimable_standard_rated_expenses > 0 + recoverable_standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 @@ -287,10 +287,10 @@ def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions(filters) return frappe.db.sql(""" - select sum(claimable_standard_rated_expenses) from + select sum(recoverable_standard_rated_expenses) from `tabPurchase Invoice` where - claimable_standard_rated_expenses > 0 + recoverable_standard_rated_expenses > 0 and docstatus = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 From 33cdfdfb7f21661997cef6fa70095f436b348054 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 19 Oct 2020 20:48:43 +0530 Subject: [PATCH 048/283] feat(UAE VAT 201): Add tests for report --- .../purchase_invoice/test_purchase_invoice.py | 3 +- .../report/uae_vat_201/test_uae_vat_201.py | 370 ++++++++++++++++++ 2 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 erpnext/regional/report/uae_vat_201/test_uae_vat_201.py diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 2e5a7142a3..8276c1fc03 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1040,7 +1040,8 @@ def make_purchase_invoice_against_cost_center(**args): pi.is_return = args.is_return pi.credit_to = args.return_against or "Creditors - _TC" pi.is_subcontracted = args.is_subcontracted or "No" - pi.supplier_warehouse = "_Test Warehouse 1 - _TC" + if args.supplier_warehouse: + pi.supplier_warehouse = "_Test Warehouse 1 - _TC" pi.append("items", { "item_code": args.item or args.item_code or "_Test Item", diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py new file mode 100644 index 0000000000..8e845d7432 --- /dev/null +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -0,0 +1,370 @@ +# coding=utf-8 +from __future__ import unicode_literals + +import erpnext +import frappe +from unittest import TestCase +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice +from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account +from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( + get_total_emiratewise, + get_tourist_tax_return_total, + get_tourist_tax_return_tax, + get_reverse_charge_total, + get_reverse_charge_tax, + get_zero_rated_total, + get_exempt_total, + get_standard_rated_expenses_total, + get_standard_rated_expenses_tax, + get_reverse_charge_recoverable_total, + get_reverse_charge_recoverable_tax + ) + + + +class TestUaeVat201(TestCase): + def setUp(self): + frappe.set_user("Administrator") + + frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company UAE VAT'") + frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company UAE VAT'") + + + make_company("_Test Company UAE VAT", "_TCUV") + set_vat_accounts() + + make_customers() + + make_supplier() + + create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT") + + + make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0}) + make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0}) + make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1}) + + make_sales_invoices() + + create_purchase_invoices() + + def test_uae_vat_201_report(self): + filters = {"company": "_Test Company UAE VAT"} + total_emiratewise = get_total_emiratewise(filters) + amounts_by_emirate = {} + for d in total_emiratewise: + emirate, amount, vat= d + amounts_by_emirate[emirate] = { + "raw_amount": amount, + "raw_vat_amount": vat, + } + self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],300) + self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5) + self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200) + self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10) + + self.assertEqual(get_tourist_tax_return_tax(filters),2) + self.assertEqual(get_reverse_charge_total(filters),250) + self.assertEqual(get_reverse_charge_tax(filters),12.5) + self.assertEqual(get_zero_rated_total(filters),100) + self.assertEqual(get_exempt_total(filters),100) + self.assertEqual(get_standard_rated_expenses_total(filters),250) + self.assertEqual(get_standard_rated_expenses_tax(filters),1) + self.assertEqual(get_reverse_charge_recoverable_total(filters),250) + self.assertEqual(get_reverse_charge_recoverable_tax(filters),12.5) + +def make_company(company_name, abbr): + if not frappe.db.exists("Company", company_name): + company = frappe.get_doc({ + "doctype": "Company", + "company_name": company_name, + "abbr": abbr, + "default_currency": "AED", + "country": "United Arab Emirates", + "create_chart_of_accounts_based_on": "Standard Template", + }) + company.insert() + else: + company = frappe.get_doc("Company", company_name) + + # indempotent + company.create_default_warehouses() + + if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}): + company.create_default_cost_center() + + company.save() + return company + +def set_vat_accounts(): + if not frappe.db.exists("UAE VAT Settings", "_Test Company UAE VAT"): + vat_accounts = frappe.get_all( + "Account", + fields=["name"], + filters = { + "company": "_Test Company UAE VAT", + "is_group":0, + "account_type": "Tax"}) + + uae_vat_accounts = [] + for d in vat_accounts: + uae_vat_accounts.append( + { + "doctype": "UAE VAT Account", + "account":d.name + }) + + frappe.get_doc({ + "company": "_Test Company UAE VAT", + "uae_vat_accounts": uae_vat_accounts, + "doctype": "UAE VAT Settings", + }).insert() + +def make_customers(): + if not frappe.db.exists("Customer", "_Test Dubai Customer"): + customer = frappe.get_doc({ + "doctype": "Customer", + "customer_name": "_Test Dubai Customer", + "customer_type": "Company", + }) + customer.insert() + else: + customer = frappe.get_doc("Customer", "_Test Dubai Customer") + + if not frappe.db.exists("Customer", "_Test Sharjah Customer"): + customer = frappe.get_doc({ + "doctype": "Customer", + "customer_name": "_Test Sharjah Customer", + "customer_type": "Company", + }) + customer.insert() + else: + customer = frappe.get_doc("Customer", "_Test Sharjah Customer") + + if not frappe.db.exists('Address', '_Test Dubai Address'): + address = frappe.get_doc({ + "address_line1": "_Test Address Line 1", + "address_title": "_Test Dubai Address", + "address_type": "Billing", + "city": "_Test City", + "state": "Test State", + "country": "United Arab Emirates", + "doctype": "Address", + "emirate": "Dubai" + }).insert() + + address.append("links", { + "link_doctype": "Customer", + "link_name": "_Test Dubai Customer" + }) + + address.save() + + if not frappe.db.exists('Address', '_Test Sharjah Address'): + address = frappe.get_doc({ + "address_line1": "_Test Address Line 1", + "address_title": "_Test Sharjah Address", + "address_type": "Billing", + "city": "_Test City", + "state": "Test State", + "country": "United Arab Emirates", + "doctype": "Address", + "emirate": "Sharjah" + }).insert() + + address.append("links", { + "link_doctype": "Customer", + "link_name": "_Test Sharjah Customer" + }) + + address.save() + +def make_supplier(): + + if not frappe.db.exists("Supplier", "_Test UAE Supplier"): + frappe.get_doc({ + "supplier_group": "Local", + "supplier_name": "_Test UAE Supplier", + "supplier_type": "Individual", + "doctype": "Supplier", + }).insert() + +def create_warehouse(warehouse_name, properties=None, company=None): + if not company: + company = "_Test Company" + + warehouse_id = erpnext.encode_company_abbr(warehouse_name, company) + if not frappe.db.exists("Warehouse", warehouse_id): + w = frappe.new_doc("Warehouse") + w.warehouse_name = warehouse_name + w.parent_warehouse = "All Warehouses - _TCUV" + w.company = company + w.account = get_warehouse_account(warehouse_name, company) + if properties: + w.update(properties) + w.save() + return w.name + else: + return warehouse_id + +def make_item(item_code, properties=None): + if frappe.db.exists("Item", item_code): + return frappe.get_doc("Item", item_code) + + item = frappe.get_doc({ + "doctype": "Item", + "item_code": item_code, + "item_name": item_code, + "description": item_code, + "item_group": "Products" + }) + + if properties: + item.update(properties) + + item.insert() + + return item + +def make_sales_invoices(): + si = create_sales_invoice(company="_Test Company UAE VAT", + customer = '_Test Dubai Customer', + currency = 'AED', + warehouse = 'Finished Goods - _TCUV', + debit_to = 'Debtors - _TCUV', + income_account = 'Sales - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + cost_center = 'Main - _TCUV', + sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", + item = "_Test UAE VAT Item", + do_not_save=1 + ) + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) + si.submit() + + si = create_sales_invoice(company="_Test Company UAE VAT", + customer = '_Test Sharjah Customer', + currency = 'AED', + warehouse = 'Finished Goods - _TCUV', + debit_to = 'Debtors - _TCUV', + income_account = 'Sales - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + cost_center = 'Main - _TCUV', + sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", + item = "_Test UAE VAT Item", + do_not_save=1 + ) + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) + si.submit() + + si = create_sales_invoice(company="_Test Company UAE VAT", + customer = '_Test Dubai Customer', + currency = 'AED', + warehouse = 'Finished Goods - _TCUV', + debit_to = 'Debtors - _TCUV', + income_account = 'Sales - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + cost_center = 'Main - _TCUV', + sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", + item = "_Test UAE VAT Item", + do_not_save=1 + ) + + si.tourist_tax_return = 2 + + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) + si.submit() + + si = create_sales_invoice(company="_Test Company UAE VAT", + customer = '_Test Sharjah Customer', + currency = 'AED', + warehouse = 'Finished Goods - _TCUV', + debit_to = 'Debtors - _TCUV', + income_account = 'Sales - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + cost_center = 'Main - _TCUV', + sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", + item = "_Test UAE VAT Zero Rated Item", + ) + + si = create_sales_invoice(company="_Test Company UAE VAT", + customer = '_Test Sharjah Customer', + currency = 'AED', + warehouse = 'Finished Goods - _TCUV', + debit_to = 'Debtors - _TCUV', + income_account = 'Sales - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + cost_center = 'Main - _TCUV', + sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", + item = "_Test UAE VAT Exempt Item", + ) + +def create_purchase_invoices(): + + pi = make_purchase_invoice( + company="_Test Company UAE VAT", + supplier = '_Test UAE Supplier', + warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', + currency = 'AED', + cost_center = 'Main - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + item = "_Test UAE VAT Item", + do_not_save=1, + ) + pi.append("taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) + + pi.recoverable_standard_rated_expenses = 1 + + pi.submit() + + pi = make_purchase_invoice( + company="_Test Company UAE VAT", + supplier = '_Test UAE Supplier', + warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', + currency = 'AED', + cost_center = 'Main - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + item = "_Test UAE VAT Item", + do_not_save=1, + ) + + pi.append("taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) + + pi.reverse_charge = "Y" + + pi.recoverable_reverse_charge = 100 + + pi.submit() + + From 853fd03f4910c9bd8641085ba876152e11f7728b Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 19 Oct 2020 22:35:16 +0530 Subject: [PATCH 049/283] feat(uae vat 201): fix linter issues --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 8e845d7432..6a255b8a0b 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -64,6 +64,7 @@ class TestUaeVat201(TestCase): self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200) self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10) + self.assertEqual(get_tourist_tax_return_total(filters),100) self.assertEqual(get_tourist_tax_return_tax(filters),2) self.assertEqual(get_reverse_charge_total(filters),250) self.assertEqual(get_reverse_charge_tax(filters),12.5) From 234036df32fc6db164cf930dbc1e37dfabf1fee6 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Tue, 20 Oct 2020 07:42:10 +0530 Subject: [PATCH 050/283] chore(uae vat 201): solve travis issues --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 6a255b8a0b..648d98e204 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -341,6 +341,7 @@ def create_purchase_invoices(): pi.recoverable_standard_rated_expenses = 1 + pi.insert() pi.submit() pi = make_purchase_invoice( @@ -366,6 +367,7 @@ def create_purchase_invoices(): pi.recoverable_reverse_charge = 100 + pi.insert() pi.submit() From c08591124c172da56a2e917644d864f4c6dc0ef5 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Tue, 20 Oct 2020 10:17:48 +0530 Subject: [PATCH 051/283] chore(UAE VAT 21): solve merge conflicts --- .../doctype/purchase_invoice/test_purchase_invoice.py | 2 +- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 8276c1fc03..f2499d24b5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -998,7 +998,7 @@ def make_purchase_invoice(**args): 'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC', "conversion_factor": 1.0, "serial_no": args.serial_no, - "stock_uom": "_Test UOM", + "stock_uom": args.uom or "_Test UOM", "cost_center": args.cost_center or "_Test Cost Center - _TC", "project": args.project, "rejected_warehouse": args.rejected_warehouse or "", diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 648d98e204..68c163d5bc 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -324,12 +324,14 @@ def create_purchase_invoices(): pi = make_purchase_invoice( company="_Test Company UAE VAT", supplier = '_Test UAE Supplier', + supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', currency = 'AED', cost_center = 'Main - _TCUV', expense_account = 'Cost of Goods Sold - _TCUV', item = "_Test UAE VAT Item", do_not_save=1, + uom = "Nos" ) pi.append("taxes", { "charge_type": "On Net Total", @@ -341,18 +343,19 @@ def create_purchase_invoices(): pi.recoverable_standard_rated_expenses = 1 - pi.insert() pi.submit() pi = make_purchase_invoice( company="_Test Company UAE VAT", supplier = '_Test UAE Supplier', + supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', currency = 'AED', cost_center = 'Main - _TCUV', expense_account = 'Cost of Goods Sold - _TCUV', item = "_Test UAE VAT Item", do_not_save=1, + uom = "Nos" ) pi.append("taxes", { @@ -367,7 +370,6 @@ def create_purchase_invoices(): pi.recoverable_reverse_charge = 100 - pi.insert() pi.submit() From 6a24da4efd860b9f32ea61bb3cfc7ab23d787909 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Tue, 20 Oct 2020 11:53:06 +0530 Subject: [PATCH 052/283] refactor(UAE VAT 201): break functions --- .../report/uae_vat_201/uae_vat_201.py | 172 +++++++----------- 1 file changed, 69 insertions(+), 103 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index b5ff8389e3..90e7501291 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -46,109 +46,8 @@ def get_columns(): def get_data(filters = None): """Returns the list of dictionaries. Each dictionary is a row in the datatable and chart data.""" data = [] - data.append({ - "no": '', - "legend": _('VAT on Sales and All Other Outputs'), - "amount": '', - "vat_amount": '' - }) - - total_emiratewise = get_total_emiratewise(filters) - emirates = get_emirates() - amounts_by_emirate = {} - for d in total_emiratewise: - emirate, amount, vat= d - amounts_by_emirate[emirate] = { - "legend": emirate, - "raw_amount": amount, - "raw_vat_amount": vat, - "amount": frappe.format(amount, 'Currency'), - "vat_amount": frappe.format(vat, 'Currency'), - } - - for d, emirate in enumerate(emirates, 97): - if emirate in amounts_by_emirate: - amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) - amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) - data.append(amounts_by_emirate[emirate]) - else: - data.append( - { - "no": _('1{0}').format(chr(d)), - "legend": _('Standard rated supplies in {0}').format(emirate), - "amount": frappe.format(0, 'Currency'), - "vat_amount": frappe.format(0, 'Currency') - } - ) - - data.append( - { - "no": '2', - "legend": _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), - "amount": frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'), - "vat_amount": frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency') - } - ) - - data.append( - { - "no": '3', - "legend": _('Supplies subject to the reverse charge provision'), - "amount": frappe.format(get_reverse_charge_total(filters), 'Currency'), - "vat_amount": frappe.format(get_reverse_charge_tax(filters), 'Currency') - } - ) - - data.append( - { - "no": '4', - "legend": _('Zero Rated'), - "amount": frappe.format(get_zero_rated_total(filters), 'Currency'), - "vat_amount": "-" - } - ) - - data.append( - { - "no": '5', - "legend": _('Exempt Supplies'), - "amount": frappe.format(get_exempt_total(filters), 'Currency'), - "vat_amount": "-" - } - ) - - data.append({ - "no": '', - "legend": '', - "amount": '', - "vat_amount": '' - }) - - data.append({ - "no": '', - "legend": _('VAT on Expenses and All Other Inputs'), - "amount": '', - "vat_amount": '' - }) - - data.append( - { - "no": '9', - "legend": _('Standard Rated Expenses'), - "amount": frappe.format(get_standard_rated_expenses_total(filters), 'Currency'), - "vat_amount": frappe.format(get_standard_rated_expenses_tax(filters), 'Currency') - } - ) - - data.append( - { - "no": '10', - "legend": _('Supplies subject to the reverse charge provision'), - "amount": frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'), - "vat_amount": frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency') - } - ) - + emirates, amounts_by_emirate = append_vat_on_sales(data, filters) + append_vat_on_expenses(data, filters) return data, emirates, amounts_by_emirate @@ -178,6 +77,73 @@ def get_chart(emirates, amounts_by_emirate): chart["fieldtype"] = "Currency" return chart +def append_vat_on_sales(data, filters): + """Appends Sales and All Other Outputs""" + append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '') + + emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters) + + append_data(data, '2', _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), + frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'), + frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency')) + + append_data(data, '3', _('Supplies subject to the reverse charge provision'), + frappe.format(get_reverse_charge_total(filters), 'Currency'), + frappe.format(get_reverse_charge_tax(filters), 'Currency')) + + append_data(data, '4', _('Zero Rated'), + frappe.format(get_zero_rated_total(filters), 'Currency'), "-") + + append_data(data, '5', _('Exempt Supplies'), + frappe.format(get_exempt_total(filters), 'Currency'),"-") + + append_data(data, '', '', '', '') + + return emirates, amounts_by_emirate + +def standard_rated_expenses_emiratewise(data, filters): + """"Append emiratewise standard rated expenses and vat""" + total_emiratewise = get_total_emiratewise(filters) + emirates = get_emirates() + amounts_by_emirate = {} + for d in total_emiratewise: + emirate, amount, vat= d + amounts_by_emirate[emirate] = { + "legend": emirate, + "raw_amount": amount, + "raw_vat_amount": vat, + "amount": frappe.format(amount, 'Currency'), + "vat_amount": frappe.format(vat, 'Currency'), + } + + for d, emirate in enumerate(emirates, 97): + if emirate in amounts_by_emirate: + amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) + amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) + data.append(amounts_by_emirate[emirate]) + else: + append_data(data, _('1{0}').format(chr(d)), + _('Standard rated supplies in {0}').format(emirate), + frappe.format(0, 'Currency'), frappe.format(0, 'Currency')) + return emirates, amounts_by_emirate + + +def append_vat_on_expenses(data, filters): + """Appends Expenses and All Other Inputs""" + append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '') + append_data(data, '9', _('Standard Rated Expenses'), + frappe.format(get_standard_rated_expenses_total(filters), 'Currency'), + frappe.format(get_standard_rated_expenses_tax(filters), 'Currency')) + + append_data(data, '10', _('Supplies subject to the reverse charge provision'), + frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'), + frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency') +) + +def append_data(data, no, legend, amount, vat_amount): + """Returns data with appended value.""" + data.append({"no": no, "legend":legend, "amount": amount, "vat_amount": vat_amount}) + def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" return frappe.db.sql(f""" From 33e9a1ab31f5d22c2a865f84bdc8aa931c071545 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Tue, 20 Oct 2020 13:04:38 +0530 Subject: [PATCH 053/283] refactor(UAE VAT 201): solve linting issues --- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 90e7501291..82032461ce 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -78,7 +78,7 @@ def get_chart(emirates, amounts_by_emirate): return chart def append_vat_on_sales(data, filters): - """Appends Sales and All Other Outputs""" + """Appends Sales and All Other Outputs.""" append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '') emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters) @@ -102,7 +102,7 @@ def append_vat_on_sales(data, filters): return emirates, amounts_by_emirate def standard_rated_expenses_emiratewise(data, filters): - """"Append emiratewise standard rated expenses and vat""" + """Append emiratewise standard rated expenses and vat""" total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} @@ -129,7 +129,7 @@ def standard_rated_expenses_emiratewise(data, filters): def append_vat_on_expenses(data, filters): - """Appends Expenses and All Other Inputs""" + """Appends Expenses and All Other Inputs.""" append_data(data, '', _('VAT on Expenses and All Other Inputs'), '', '') append_data(data, '9', _('Standard Rated Expenses'), frappe.format(get_standard_rated_expenses_total(filters), 'Currency'), From 8796567b4207195a4b24353470595e39ce1e56fd Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Tue, 20 Oct 2020 16:35:28 +0530 Subject: [PATCH 054/283] chore(UAE VAT 201): add puctuation --- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 2 +- erpnext/regional/united_arab_emirates/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 82032461ce..5bed0e2463 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -102,7 +102,7 @@ def append_vat_on_sales(data, filters): return emirates, amounts_by_emirate def standard_rated_expenses_emiratewise(data, filters): - """Append emiratewise standard rated expenses and vat""" + """Append emiratewise standard rated expenses and vat.""" total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index efa39bd3c5..fac6e270db 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -89,7 +89,7 @@ def make_custom_fields(): dict(fieldname='is_zero_rated', label='Is Zero Rated', fieldtype='Check', insert_after='tax_code', print_hide=1), - dict(fieldname='is_exempt', label='Is Exempt ', + dict(fieldname='is_exempt', label='Is Exempt', fieldtype='Check', insert_after='is_zero_rated', print_hide=1) ], From 0ac40c75aabbbe2ee31dfd0b4f4881e9846dcf6d Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Sun, 25 Oct 2020 01:16:06 +0530 Subject: [PATCH 055/283] chore(UAE VAT): update tesr dependencies --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 68c163d5bc..05bb701dc2 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -21,7 +21,7 @@ from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( get_reverse_charge_recoverable_tax ) - +test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"] class TestUaeVat201(TestCase): def setUp(self): From 0ceae0bd66e7bbd1f8af8f4b20bc50450fbc2202 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Sun, 25 Oct 2020 13:06:42 +0530 Subject: [PATCH 056/283] feat(UAE VAT 201): emirate customizable --- .../report/uae_vat_201/test_uae_vat_201.py | 79 +++++-------------- .../regional/united_arab_emirates/setup.py | 10 +-- 2 files changed, 23 insertions(+), 66 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 05bb701dc2..ceaa3b3dcb 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -34,13 +34,12 @@ class TestUaeVat201(TestCase): make_company("_Test Company UAE VAT", "_TCUV") set_vat_accounts() - make_customers() + make_customer() make_supplier() create_warehouse("_Test UAE VAT Supplier Warehouse", company="_Test Company UAE VAT") - make_item("_Test UAE VAT Item", properties = {"is_zero_rated": 0, "is_exempt": 0}) make_item("_Test UAE VAT Zero Rated Item", properties = {"is_zero_rated": 1, "is_exempt": 0}) make_item("_Test UAE VAT Exempt Item", properties = {"is_zero_rated": 0, "is_exempt": 1}) @@ -122,64 +121,16 @@ def set_vat_accounts(): "doctype": "UAE VAT Settings", }).insert() -def make_customers(): - if not frappe.db.exists("Customer", "_Test Dubai Customer"): +def make_customer(): + if not frappe.db.exists("Customer", "_Test UAE Customer"): customer = frappe.get_doc({ "doctype": "Customer", - "customer_name": "_Test Dubai Customer", + "customer_name": "_Test UAE Customer", "customer_type": "Company", }) customer.insert() else: - customer = frappe.get_doc("Customer", "_Test Dubai Customer") - - if not frappe.db.exists("Customer", "_Test Sharjah Customer"): - customer = frappe.get_doc({ - "doctype": "Customer", - "customer_name": "_Test Sharjah Customer", - "customer_type": "Company", - }) - customer.insert() - else: - customer = frappe.get_doc("Customer", "_Test Sharjah Customer") - - if not frappe.db.exists('Address', '_Test Dubai Address'): - address = frappe.get_doc({ - "address_line1": "_Test Address Line 1", - "address_title": "_Test Dubai Address", - "address_type": "Billing", - "city": "_Test City", - "state": "Test State", - "country": "United Arab Emirates", - "doctype": "Address", - "emirate": "Dubai" - }).insert() - - address.append("links", { - "link_doctype": "Customer", - "link_name": "_Test Dubai Customer" - }) - - address.save() - - if not frappe.db.exists('Address', '_Test Sharjah Address'): - address = frappe.get_doc({ - "address_line1": "_Test Address Line 1", - "address_title": "_Test Sharjah Address", - "address_type": "Billing", - "city": "_Test City", - "state": "Test State", - "country": "United Arab Emirates", - "doctype": "Address", - "emirate": "Sharjah" - }).insert() - - address.append("links", { - "link_doctype": "Customer", - "link_name": "_Test Sharjah Customer" - }) - - address.save() + customer = frappe.get_doc("Customer", "_Test UAE Customer") def make_supplier(): @@ -230,7 +181,7 @@ def make_item(item_code, properties=None): def make_sales_invoices(): si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test Dubai Customer', + customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', debit_to = 'Debtors - _TCUV', @@ -248,10 +199,11 @@ def make_sales_invoices(): "description": "VAT 5% @ 5.0", "rate": 5.0 }) + si.emirate = 'Dubai' si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test Sharjah Customer', + customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', debit_to = 'Debtors - _TCUV', @@ -262,6 +214,7 @@ def make_sales_invoices(): item = "_Test UAE VAT Item", do_not_save=1 ) + si.emirate = 'Sharjah' si.append("taxes", { "charge_type": "On Net Total", "account_head": "VAT 5% - _TCUV", @@ -272,7 +225,7 @@ def make_sales_invoices(): si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test Dubai Customer', + customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', debit_to = 'Debtors - _TCUV', @@ -286,6 +239,8 @@ def make_sales_invoices(): si.tourist_tax_return = 2 + si.emirate = 'Dubai' + si.append("taxes", { "charge_type": "On Net Total", "account_head": "VAT 5% - _TCUV", @@ -296,7 +251,7 @@ def make_sales_invoices(): si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test Sharjah Customer', + customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', debit_to = 'Debtors - _TCUV', @@ -305,10 +260,13 @@ def make_sales_invoices(): cost_center = 'Main - _TCUV', sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", item = "_Test UAE VAT Zero Rated Item", + do_not_save=1 ) + si.emirate = 'Sharjah' + si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test Sharjah Customer', + customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', debit_to = 'Debtors - _TCUV', @@ -317,7 +275,10 @@ def make_sales_invoices(): cost_center = 'Main - _TCUV', sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", item = "_Test UAE VAT Exempt Item", + do_not_save=1 ) + si.emirate = 'Sharjah' + si.submit() def create_purchase_invoices(): diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index fac6e270db..7f890d6867 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -56,10 +56,10 @@ def make_custom_fields(): dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), - dict(fieldname='emirate', label='Emirate', insert_after='customer_address', - fieldtype='Read Only', fetch_from='customer_address.emirate'), + dict(fieldname='emirate', label='Emirate', insert_after='permit_no', fieldtype='Select', + options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', - insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), + insert_after='emirate', fieldtype='Currency', print_hide=1, default='0'), ] invoice_item_fields = [ @@ -101,10 +101,6 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Data', insert_after='supplier_name'), ], - 'Address': [ - dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', - options='Abu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') - ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, 'Purchase Receipt': purchase_invoice_fields + invoice_fields, From 5953acab8bb4a9efbbca7649db62d3fce38779f0 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Sun, 25 Oct 2020 15:43:21 +0530 Subject: [PATCH 057/283] chore(UAE VAT 201): cleanup --- .../report/uae_vat_201/test_uae_vat_201.py | 40 +------------------ .../regional/united_arab_emirates/setup.py | 3 +- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index ceaa3b3dcb..3b5d388e25 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -11,14 +11,10 @@ from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( get_total_emiratewise, get_tourist_tax_return_total, get_tourist_tax_return_tax, - get_reverse_charge_total, - get_reverse_charge_tax, get_zero_rated_total, get_exempt_total, get_standard_rated_expenses_total, get_standard_rated_expenses_tax, - get_reverse_charge_recoverable_total, - get_reverse_charge_recoverable_tax ) test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"] @@ -62,17 +58,12 @@ class TestUaeVat201(TestCase): self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5) self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200) self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10) - self.assertEqual(get_tourist_tax_return_total(filters),100) self.assertEqual(get_tourist_tax_return_tax(filters),2) - self.assertEqual(get_reverse_charge_total(filters),250) - self.assertEqual(get_reverse_charge_tax(filters),12.5) self.assertEqual(get_zero_rated_total(filters),100) self.assertEqual(get_exempt_total(filters),100) self.assertEqual(get_standard_rated_expenses_total(filters),250) self.assertEqual(get_standard_rated_expenses_tax(filters),1) - self.assertEqual(get_reverse_charge_recoverable_total(filters),250) - self.assertEqual(get_reverse_charge_recoverable_tax(filters),12.5) def make_company(company_name, abbr): if not frappe.db.exists("Company", company_name): @@ -304,33 +295,4 @@ def create_purchase_invoices(): pi.recoverable_standard_rated_expenses = 1 - pi.submit() - - pi = make_purchase_invoice( - company="_Test Company UAE VAT", - supplier = '_Test UAE Supplier', - supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', - warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', - currency = 'AED', - cost_center = 'Main - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - item = "_Test UAE VAT Item", - do_not_save=1, - uom = "Nos" - ) - - pi.append("taxes", { - "charge_type": "On Net Total", - "account_head": "VAT 5% - _TCUV", - "cost_center": "Main - _TCUV", - "description": "VAT 5% @ 5.0", - "rate": 5.0 - }) - - pi.reverse_charge = "Y" - - pi.recoverable_reverse_charge = 100 - - pi.submit() - - + pi.submit() \ No newline at end of file diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 7f890d6867..c1188d6ccd 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -39,8 +39,7 @@ def make_custom_fields(): fieldtype='Read Only', insert_after='supplier_name', fetch_from='supplier.supplier_name_in_arabic', print_hide=1), dict(fieldname='recoverable_standard_rated_expenses', label='Recoverable Standard Rated Expenses (AED)', - insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0', - depends_on="eval:doc.reverse_charge=='N'",), + insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), dict(fieldname='reverse_charge', label='Reverse Charge Applicable', fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1, options='Y\nN', default='N'), From 81f2efd9b04b307af66b45cf69ca8a06021617f5 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 28 Oct 2020 10:28:25 +0530 Subject: [PATCH 058/283] chore(UAE VAT 201): Rename emirate to vat emirate --- .../regional/report/uae_vat_201/test_uae_vat_201.py | 10 +++++----- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 4 ++-- erpnext/regional/united_arab_emirates/setup.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 3b5d388e25..3037a07698 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -190,7 +190,7 @@ def make_sales_invoices(): "description": "VAT 5% @ 5.0", "rate": 5.0 }) - si.emirate = 'Dubai' + si.vat_emirate = 'Dubai' si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", @@ -205,7 +205,7 @@ def make_sales_invoices(): item = "_Test UAE VAT Item", do_not_save=1 ) - si.emirate = 'Sharjah' + si.vat_emirate = 'Sharjah' si.append("taxes", { "charge_type": "On Net Total", "account_head": "VAT 5% - _TCUV", @@ -230,7 +230,7 @@ def make_sales_invoices(): si.tourist_tax_return = 2 - si.emirate = 'Dubai' + si.vat_emirate = 'Dubai' si.append("taxes", { "charge_type": "On Net Total", @@ -253,7 +253,7 @@ def make_sales_invoices(): item = "_Test UAE VAT Zero Rated Item", do_not_save=1 ) - si.emirate = 'Sharjah' + si.vat_emirate = 'Sharjah' si.submit() si = create_sales_invoice(company="_Test Company UAE VAT", @@ -268,7 +268,7 @@ def make_sales_invoices(): item = "_Test UAE VAT Exempt Item", do_not_save=1 ) - si.emirate = 'Sharjah' + si.vat_emirate = 'Sharjah' si.submit() def create_purchase_invoices(): diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 5bed0e2463..ddbdf96c6a 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -147,9 +147,9 @@ def append_data(data, no, legend, amount, vat_amount): def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" return frappe.db.sql(f""" - select emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` + select vat_emirate as emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` where docstatus = 1 {get_conditions(filters)} - group by `tabSales Invoice`.emirate; + group by `tabSales Invoice`.vat_emirate; """, filters) def get_emirates(): diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index c1188d6ccd..62156e46be 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -55,10 +55,10 @@ def make_custom_fields(): dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), - dict(fieldname='emirate', label='Emirate', insert_after='permit_no', fieldtype='Select', + dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select', options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', - insert_after='emirate', fieldtype='Currency', print_hide=1, default='0'), + insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'), ] invoice_item_fields = [ From 2c00549dfe9d87ed1b21a600df7275d0459c86c8 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 28 Oct 2020 22:54:56 +0530 Subject: [PATCH 059/283] refactor(UAE VAT 201): Use frappe api instead sql --- .../report/uae_vat_201/uae_vat_201.py | 152 ++++++++++-------- 1 file changed, 86 insertions(+), 66 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index ddbdf96c6a..744eb87d55 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -146,11 +146,14 @@ def append_data(data, no, legend, amount, vat_amount): def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" - return frappe.db.sql(f""" - select vat_emirate as emirate, sum(total), sum(total_taxes_and_charges) from `tabSales Invoice` - where docstatus = 1 {get_conditions(filters)} - group by `tabSales Invoice`.vat_emirate; - """, filters) + query_filters = get_filters(filters) + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Sales Invoice', + filters = query_filters, + fields = ['vat_emirate as emirate','sum(total)', 'sum(total_taxes_and_charges)'], + group_by='vat_emirate', + as_list=True + ) def get_emirates(): """Returns a List of emirates in the order that they are to be displayed.""" @@ -174,28 +177,40 @@ def get_conditions(filters): conditions += opts[1] return conditions +def get_filters(filters): + """The conditions to be used to filter data to calculate the total sale.""" + query_filters = {} + if filters.get("company"): + query_filters["company"] = ['=', filters['company']] + if filters.get("from_date"): + query_filters["posting_date"] = ['>=', filters['from_date']] + if filters.get("from_date"): + query_filters["posting_date"] = ['<=', filters['to_date']] + return query_filters + def get_reverse_charge_total(filters): """Returns the sum of the total of each Purchase invoice made.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(total) from - `tabPurchase Invoice` - where - reverse_charge = "Y" - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['reverse_charge'] = ['=', 'Y'] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_reverse_charge_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" select sum(debit) from - `tabPurchase Invoice` inner join `tabGL Entry` - on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name + `tabPurchase Invoice` p inner join `tabGL Entry` gl + on gl.voucher_no = p.name where - `tabPurchase Invoice`.reverse_charge = "Y" - and `tabPurchase Invoice`.docstatus = 1 - and `tabGL Entry`.docstatus = 1 + p.reverse_charge = "Y" + and p.docstatus = 1 + and gl.docstatus = 1 and account in (select account from `tabUAE VAT Account` where parent=%(company)s) {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 @@ -212,75 +227,80 @@ def get_conditions_join(filters): def get_reverse_charge_recoverable_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(total) from - `tabPurchase Invoice` - where - reverse_charge = "Y" - and recoverable_reverse_charge > 0 - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['reverse_charge'] = ['=', 'Y'] + query_filters['recoverable_reverse_charge'] = ['>', '0'] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_reverse_charge_recoverable_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" - select sum(debit * `tabPurchase Invoice`.recoverable_reverse_charge / 100) from - `tabPurchase Invoice` inner join `tabGL Entry` - on `tabGL Entry`.voucher_no = `tabPurchase Invoice`.name + select sum(debit * p.recoverable_reverse_charge / 100) from + `tabPurchase Invoice` p inner join `tabGL Entry` gl + on gl.voucher_no = p.name where - `tabPurchase Invoice`.reverse_charge = "Y" - and `tabPurchase Invoice`.docstatus = 1 - and `tabPurchase Invoice`.recoverable_reverse_charge > 0 - and `tabGL Entry`.docstatus = 1 + p.reverse_charge = "Y" + and p.docstatus = 1 + and p.recoverable_reverse_charge > 0 + and gl.docstatus = 1 and account in (select account from `tabUAE VAT Account` where parent=%(company)s) {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 def get_standard_rated_expenses_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(total) from - `tabPurchase Invoice` - where - recoverable_standard_rated_expenses > 0 - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['recoverable_standard_rated_expenses'] = ['>', 0] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(recoverable_standard_rated_expenses) from - `tabPurchase Invoice` - where - recoverable_standard_rated_expenses > 0 - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['recoverable_standard_rated_expenses'] = ['>', 0] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Purchase Invoice', + filters = query_filters, + fields = ['sum(recoverable_standard_rated_expenses)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_tourist_tax_return_total(filters): """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(total) from - `tabSales Invoice` - where - tourist_tax_return > 0 - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['tourist_tax_return'] = ['>', 0] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Sales Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_tourist_tax_return_tax(filters): """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return.""" - conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(tourist_tax_return) from - `tabSales Invoice` - where - tourist_tax_return > 0 - and docstatus = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + query_filters = get_filters(filters) + query_filters['tourist_tax_return'] = ['>', 0] + query_filters['docstatus'] = ['=', 1] + return frappe.db.get_list('Sales Invoice', + filters = query_filters, + fields = ['sum(tourist_tax_return)'], + as_list=True, + limit = 1 + )[0][0] or 0 def get_zero_rated_total(filters): """Returns the sum of each Sales Invoice Item Amount which is zero rated.""" From 08846f6b11d88a0cd81888dc7747d3da992cff16 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 28 Oct 2020 23:33:56 +0530 Subject: [PATCH 060/283] refactor(UAE VAT 201): replace cartesian product to inner joins --- .../report/uae_vat_201/uae_vat_201.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 744eb87d55..ba6ff69361 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -9,7 +9,6 @@ def execute(filters=None): columns = get_columns() data, emirates, amounts_by_emirate = get_data(filters) chart = get_chart(emirates, amounts_by_emirate) - return columns, data, None, chart def get_columns(): @@ -50,7 +49,6 @@ def get_data(filters = None): append_vat_on_expenses(data, filters) return data, emirates, amounts_by_emirate - def get_chart(emirates, amounts_by_emirate): """Returns chart data.""" labels = [] @@ -167,16 +165,6 @@ def get_emirates(): 'Fujairah' ] -def get_conditions(filters): - """The conditions to be used to filter data to calculate the total sale.""" - conditions = "" - for opts in (("company", " and company=%(company)s"), - ("from_date", " and posting_date>=%(from_date)s"), - ("to_date", " and posting_date<=%(to_date)s")): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - def get_filters(filters): """The conditions to be used to filter data to calculate the total sale.""" query_filters = {} @@ -215,16 +203,6 @@ def get_reverse_charge_tax(filters): {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 -def get_conditions_join(filters): - """The conditions to be used to filter data to calculate the total vat.""" - conditions = "" - for opts in (("company", " and `tabPurchase Invoice`.company=%(company)s"), - ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), - ("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s")): - if filters.get(opts[0]): - conditions += opts[1] - return conditions - def get_reverse_charge_recoverable_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" query_filters = get_filters(filters) @@ -254,6 +232,16 @@ def get_reverse_charge_recoverable_tax(filters): {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 +def get_conditions_join(filters): + """The conditions to be used to filter data to calculate the total vat.""" + conditions = "" + for opts in (("company", " and `tabPurchase Invoice`.company=%(company)s"), + ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), + ("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s")): + if filters.get(opts[0]): + conditions += opts[1] + return conditions + def get_standard_rated_expenses_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" query_filters = get_filters(filters) @@ -307,8 +295,9 @@ def get_zero_rated_total(filters): conditions = get_conditions(filters) return frappe.db.sql(""" select sum(i.base_amount) as total from - `tabSales Invoice Item` i, `tabSales Invoice` s - where s.docstatus = 1 and i.parent = s.name and i.is_zero_rated = 1 + `tabSales Invoice Item` i inner join `tabSales Invoice` s + on i.parent = s.name + where s.docstatus = 1 and i.is_zero_rated = 1 {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 @@ -317,7 +306,18 @@ def get_exempt_total(filters): conditions = get_conditions(filters) return frappe.db.sql(""" select sum(i.base_amount) as total from - `tabSales Invoice Item` i, `tabSales Invoice` s - where s.docstatus = 1 and i.parent = s.name and i.is_exempt = 1 + `tabSales Invoice Item` i inner join `tabSales Invoice` s + on i.parent = s.name + where s.docstatus = 1 and i.is_exempt = 1 {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 \ No newline at end of file + """.format(where_conditions=conditions), filters)[0][0] or 0 + +def get_conditions(filters): + """The conditions to be used to filter data to calculate the total sale.""" + conditions = "" + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if filters.get(opts[0]): + conditions += opts[1] + return conditions \ No newline at end of file From bf261a6c394569ec34874a28eff7be511e67ba88 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Thu, 29 Oct 2020 00:07:52 +0530 Subject: [PATCH 061/283] fix(UAE VAT 201): fix helper --- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index ba6ff69361..758da4205a 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -235,9 +235,9 @@ def get_reverse_charge_recoverable_tax(filters): def get_conditions_join(filters): """The conditions to be used to filter data to calculate the total vat.""" conditions = "" - for opts in (("company", " and `tabPurchase Invoice`.company=%(company)s"), - ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), - ("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s")): + for opts in (("company", " and p.company=%(company)s"), + ("from_date", " and p.posting_date>=%(from_date)s"), + ("to_date", " and p.posting_date<=%(to_date)s")): if filters.get(opts[0]): conditions += opts[1] return conditions From eb3084b072654573ff4dd3ad97590f15878e48b0 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Thu, 29 Oct 2020 09:52:17 +0530 Subject: [PATCH 062/283] refactor(UAE VAT 201): break functions --- .../report/uae_vat_201/uae_vat_201.py | 13 +++++++ .../regional/united_arab_emirates/utils.py | 38 ++++++++++--------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 758da4205a..216762bc8d 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -113,6 +113,7 @@ def standard_rated_expenses_emiratewise(data, filters): "amount": frappe.format(amount, 'Currency'), "vat_amount": frappe.format(vat, 'Currency'), } + amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate) for d, emirate in enumerate(emirates, 97): if emirate in amounts_by_emirate: @@ -125,6 +126,18 @@ def standard_rated_expenses_emiratewise(data, filters): frappe.format(0, 'Currency'), frappe.format(0, 'Currency')) return emirates, amounts_by_emirate +def append_emiratewise_expenses(data, emirates, amounts_by_emirate): + """Append emiratewise standard rated expenses and vat.""" + for d, emirate in enumerate(emirates, 97): + if emirate in amounts_by_emirate: + amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) + amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) + data.append(amounts_by_emirate[emirate]) + else: + append_data(data, _('1{0}').format(chr(d)), + _('Standard rated supplies in {0}').format(emirate), + frappe.format(0, 'Currency'), frappe.format(0, 'Currency')) + return amounts_by_emirate def append_vat_on_expenses(data, filters): """Appends Expenses and All Other Inputs.""" diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 7edc2b6720..7430db4fce 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -123,25 +123,29 @@ def make_regional_gl_entries(gl_entries, doc): for tax in doc.get('taxes'): if tax.category not in ("Total", "Valuation and Total"): continue - - dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts: - account_currency = get_account_currency(tax.account_head) - - gl_entries.append(doc.get_gl_dict( - { - "account": tax.account_head, - "cost_center": tax.cost_center, - "posting_date": doc.posting_date, - "against": doc.supplier, - dr_or_cr: tax.base_tax_amount_after_discount_amount, - dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \ - if account_currency==doc.company_currency \ - else tax.tax_amount_after_discount_amount - }, account_currency, item=tax) - ) + gl_entries = make_gl_entry(tax, gl_entries, doc, tax_accounts) return gl_entries +def make_gl_entry(tax, gl_entries, doc, tax_accounts): + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts: + account_currency = get_account_currency(tax.account_head) + + gl_entries.append(doc.get_gl_dict( + { + "account": tax.account_head, + "cost_center": tax.cost_center, + "posting_date": doc.posting_date, + "against": doc.supplier, + dr_or_cr: tax.base_tax_amount_after_discount_amount, + dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=tax) + ) + return gl_entries + + def validate_returns(doc, method): """Standard Rated expenses should not be set when Reverse Charge Applicable is set.""" country = frappe.get_cached_value('Company', doc.company, 'country') From 55c78b17bab76b9feb6aa5bb870858ad9e7df93c Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Thu, 29 Oct 2020 12:43:43 +0530 Subject: [PATCH 063/283] fix(UAE VAT 201): change desk page record --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index db01254919..64e2b246a7 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -83,7 +83,7 @@ { "hidden": 0, "label": "Value-Added Tax (VAT UAE)", - "links": "[\n {\n \"label\": \"UAE VAT Setting\",\n \"name\": \"UAE VAT Setting\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -158,4 +158,4 @@ "type": "Dashboard" } ] -} \ No newline at end of file +} From f116fbf84d9e4ec6c6d8694463faa96d0e7c1ee0 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 2 Nov 2020 10:27:54 +0530 Subject: [PATCH 064/283] refactor(UAE VAT 201): Update erpnext/regional/report/uae_vat_201/test_uae_vat_201.py Co-authored-by: Shivam Mishra --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 3037a07698..70c6b74f5e 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -15,7 +15,7 @@ from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( get_exempt_total, get_standard_rated_expenses_total, get_standard_rated_expenses_tax, - ) +) test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"] @@ -295,4 +295,4 @@ def create_purchase_invoices(): pi.recoverable_standard_rated_expenses = 1 - pi.submit() \ No newline at end of file + pi.submit() From be7a4c8b0aaf5d33e23bbb6f635f47484425637d Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 2 Nov 2020 10:28:14 +0530 Subject: [PATCH 065/283] refactor(UAE VAT 201): Update erpnext/regional/report/uae_vat_201/test_uae_vat_201.py Co-authored-by: Shivam Mishra --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 70c6b74f5e..84db9146cc 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -95,8 +95,10 @@ def set_vat_accounts(): fields=["name"], filters = { "company": "_Test Company UAE VAT", - "is_group":0, - "account_type": "Tax"}) + "is_group": 0, + "account_type": "Tax" + } + ) uae_vat_accounts = [] for d in vat_accounts: From 46265d3fc7e1101cea4960e5b593b393ada9deb7 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 2 Nov 2020 10:28:41 +0530 Subject: [PATCH 066/283] refactor(UAE VAT 201): Update erpnext/regional/report/uae_vat_201/test_uae_vat_201.py Co-authored-by: Shivam Mishra --- .../regional/report/uae_vat_201/test_uae_vat_201.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 84db9146cc..7514652894 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -48,12 +48,12 @@ class TestUaeVat201(TestCase): filters = {"company": "_Test Company UAE VAT"} total_emiratewise = get_total_emiratewise(filters) amounts_by_emirate = {} - for d in total_emiratewise: - emirate, amount, vat= d + for data in total_emiratewise: + emirate, amount, vat = data amounts_by_emirate[emirate] = { - "raw_amount": amount, - "raw_vat_amount": vat, - } + "raw_amount": amount, + "raw_vat_amount": vat, + } self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],300) self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5) self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200) From 7b771d139c66ce2251941349ffb4e4db88ce47c9 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 2 Nov 2020 10:29:14 +0530 Subject: [PATCH 067/283] refactor(UAE VAT 201): Update erpnext/regional/report/uae_vat_201/test_uae_vat_201.py Co-authored-by: Shivam Mishra --- erpnext/regional/report/uae_vat_201/test_uae_vat_201.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 7514652894..5fdb2645f7 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -101,12 +101,11 @@ def set_vat_accounts(): ) uae_vat_accounts = [] - for d in vat_accounts: - uae_vat_accounts.append( - { + for account in vat_accounts: + uae_vat_accounts.append({ "doctype": "UAE VAT Account", - "account":d.name - }) + "account": account.name + }) frappe.get_doc({ "company": "_Test Company UAE VAT", From d322181fd7d2d6cce9301b41094b314803ce7973 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 2 Nov 2020 10:29:38 +0530 Subject: [PATCH 068/283] refactor(UAE VAT 201): Update erpnext/regional/report/uae_vat_201/uae_vat_201.py Co-authored-by: Shivam Mishra --- .../regional/report/uae_vat_201/uae_vat_201.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 216762bc8d..201ddf63f5 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -65,14 +65,14 @@ def get_chart(emirates, amounts_by_emirate): datasets.append({'name': _('Vat Amount (AED)'), 'values': vat_amount}) chart = { - "data": { - 'labels': labels, - 'datasets': datasets - } + "type": "bar", + "fieldtype": "Currency" + "data": { + 'labels': labels, + 'datasets': datasets } - - chart["type"] = "bar" - chart["fieldtype"] = "Currency" + } + return chart def append_vat_on_sales(data, filters): @@ -333,4 +333,4 @@ def get_conditions(filters): ("to_date", " and posting_date<=%(to_date)s")): if filters.get(opts[0]): conditions += opts[1] - return conditions \ No newline at end of file + return conditions From c53237f5e0177a5e2a5aef39e476a58077067e9b Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 2 Nov 2020 11:02:41 +0530 Subject: [PATCH 069/283] refactor(UAE VAT 201): solve syntax errors --- .../report/uae_vat_201/uae_vat_201.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 201ddf63f5..f641e652a6 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -66,13 +66,13 @@ def get_chart(emirates, amounts_by_emirate): chart = { "type": "bar", - "fieldtype": "Currency" + "fieldtype": "Currency", "data": { 'labels': labels, 'datasets': datasets } } - + return chart def append_vat_on_sales(data, filters): @@ -89,14 +89,14 @@ def append_vat_on_sales(data, filters): frappe.format(get_reverse_charge_total(filters), 'Currency'), frappe.format(get_reverse_charge_tax(filters), 'Currency')) - append_data(data, '4', _('Zero Rated'), + append_data(data, '4', _('Zero Rated'), frappe.format(get_zero_rated_total(filters), 'Currency'), "-") append_data(data, '5', _('Exempt Supplies'), frappe.format(get_exempt_total(filters), 'Currency'),"-") append_data(data, '', '', '', '') - + return emirates, amounts_by_emirate def standard_rated_expenses_emiratewise(data, filters): @@ -159,7 +159,7 @@ def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" query_filters = get_filters(filters) query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', + return frappe.db.get_list('Sales Invoice', filters = query_filters, fields = ['vat_emirate as emirate','sum(total)', 'sum(total_taxes_and_charges)'], group_by='vat_emirate', @@ -194,7 +194,7 @@ def get_reverse_charge_total(filters): query_filters = get_filters(filters) query_filters['reverse_charge'] = ['=', 'Y'] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', + return frappe.db.get_list('Purchase Invoice', filters = query_filters, fields = ['sum(total)'], as_list=True, @@ -222,7 +222,7 @@ def get_reverse_charge_recoverable_total(filters): query_filters['reverse_charge'] = ['=', 'Y'] query_filters['recoverable_reverse_charge'] = ['>', '0'] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', + return frappe.db.get_list('Purchase Invoice', filters = query_filters, fields = ['sum(total)'], as_list=True, @@ -260,7 +260,7 @@ def get_standard_rated_expenses_total(filters): query_filters = get_filters(filters) query_filters['recoverable_standard_rated_expenses'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', + return frappe.db.get_list('Purchase Invoice', filters = query_filters, fields = ['sum(total)'], as_list=True, @@ -272,7 +272,7 @@ def get_standard_rated_expenses_tax(filters): query_filters = get_filters(filters) query_filters['recoverable_standard_rated_expenses'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', + return frappe.db.get_list('Purchase Invoice', filters = query_filters, fields = ['sum(recoverable_standard_rated_expenses)'], as_list=True, @@ -284,7 +284,7 @@ def get_tourist_tax_return_total(filters): query_filters = get_filters(filters) query_filters['tourist_tax_return'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', + return frappe.db.get_list('Sales Invoice', filters = query_filters, fields = ['sum(total)'], as_list=True, @@ -296,7 +296,7 @@ def get_tourist_tax_return_tax(filters): query_filters = get_filters(filters) query_filters['tourist_tax_return'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', + return frappe.db.get_list('Sales Invoice', filters = query_filters, fields = ['sum(tourist_tax_return)'], as_list=True, From b0eca23485b9319754e33af11727605df2f97c82 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 2 Nov 2020 12:47:10 +0530 Subject: [PATCH 070/283] Refactor(UAE VAT 201): Fix fomatiing --- .../report/uae_vat_201/test_uae_vat_201.py | 184 +++++--------- .../report/uae_vat_201/uae_vat_201.py | 227 ++++++++++-------- .../regional/united_arab_emirates/utils.py | 22 +- 3 files changed, 201 insertions(+), 232 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index 5fdb2645f7..b0a21de613 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -8,8 +8,8 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse_account from erpnext.regional.report.uae_vat_201.uae_vat_201 import ( - get_total_emiratewise, - get_tourist_tax_return_total, + get_total_emiratewise, + get_tourist_tax_return_total, get_tourist_tax_return_tax, get_zero_rated_total, get_exempt_total, @@ -26,7 +26,6 @@ class TestUaeVat201(TestCase): frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company UAE VAT'") frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company UAE VAT'") - make_company("_Test Company UAE VAT", "_TCUV") set_vat_accounts() @@ -79,7 +78,6 @@ def make_company(company_name, abbr): else: company = frappe.get_doc("Company", company_name) - # indempotent company.create_default_warehouses() if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": company.name}): @@ -95,7 +93,7 @@ def set_vat_accounts(): fields=["name"], filters = { "company": "_Test Company UAE VAT", - "is_group": 0, + "is_group": 0, "account_type": "Tax" } ) @@ -103,7 +101,7 @@ def set_vat_accounts(): uae_vat_accounts = [] for account in vat_accounts: uae_vat_accounts.append({ - "doctype": "UAE VAT Account", + "doctype": "UAE VAT Account", "account": account.name }) @@ -125,7 +123,6 @@ def make_customer(): customer = frappe.get_doc("Customer", "_Test UAE Customer") def make_supplier(): - if not frappe.db.exists("Supplier", "_Test UAE Supplier"): frappe.get_doc({ "supplier_group": "Local", @@ -140,15 +137,15 @@ def create_warehouse(warehouse_name, properties=None, company=None): warehouse_id = erpnext.encode_company_abbr(warehouse_name, company) if not frappe.db.exists("Warehouse", warehouse_id): - w = frappe.new_doc("Warehouse") - w.warehouse_name = warehouse_name - w.parent_warehouse = "All Warehouses - _TCUV" - w.company = company - w.account = get_warehouse_account(warehouse_name, company) + warehouse = frappe.new_doc("Warehouse") + warehouse.warehouse_name = warehouse_name + warehouse.parent_warehouse = "All Warehouses - _TCUV" + warehouse.company = company + warehouse.account = get_warehouse_account(warehouse_name, company) if properties: - w.update(properties) - w.save() - return w.name + warehouse.update(properties) + warehouse.save() + return warehouse.name else: return warehouse_id @@ -166,13 +163,15 @@ def make_item(item_code, properties=None): if properties: item.update(properties) - + item.insert() return item def make_sales_invoices(): - si = create_sales_invoice(company="_Test Company UAE VAT", + def make_sales_invoices_wrapper(emirate, item, tax = True, tourist_tax= False): + si = create_sales_invoice( + company="_Test Company UAE VAT", customer = '_Test UAE Customer', currency = 'AED', warehouse = 'Finished Goods - _TCUV', @@ -180,119 +179,60 @@ def make_sales_invoices(): income_account = 'Sales - _TCUV', expense_account = 'Cost of Goods Sold - _TCUV', cost_center = 'Main - _TCUV', - sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", - item = "_Test UAE VAT Item", + item = item, do_not_save=1 ) - si.append("taxes", { - "charge_type": "On Net Total", - "account_head": "VAT 5% - _TCUV", - "cost_center": "Main - _TCUV", - "description": "VAT 5% @ 5.0", - "rate": 5.0 - }) - si.vat_emirate = 'Dubai' - si.submit() + si.vat_emirate = emirate + if tax: + si.append( + "taxes", { + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + } + ) + if tourist_tax: + si.tourist_tax_return = 2 + si.submit() - si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test UAE Customer', - currency = 'AED', - warehouse = 'Finished Goods - _TCUV', - debit_to = 'Debtors - _TCUV', - income_account = 'Sales - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - cost_center = 'Main - _TCUV', - sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", - item = "_Test UAE VAT Item", - do_not_save=1 - ) - si.vat_emirate = 'Sharjah' - si.append("taxes", { - "charge_type": "On Net Total", - "account_head": "VAT 5% - _TCUV", - "cost_center": "Main - _TCUV", - "description": "VAT 5% @ 5.0", - "rate": 5.0 - }) - si.submit() + #Define Item Names + uae_item = "_Test UAE VAT Item" + uae_exempt_item = "_Test UAE VAT Exempt Item" + uae_zero_rated_item = "_Test UAE VAT Zero Rated Item" - si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test UAE Customer', - currency = 'AED', - warehouse = 'Finished Goods - _TCUV', - debit_to = 'Debtors - _TCUV', - income_account = 'Sales - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - cost_center = 'Main - _TCUV', - sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", - item = "_Test UAE VAT Item", - do_not_save=1 - ) - - si.tourist_tax_return = 2 - - si.vat_emirate = 'Dubai' - - si.append("taxes", { - "charge_type": "On Net Total", - "account_head": "VAT 5% - _TCUV", - "cost_center": "Main - _TCUV", - "description": "VAT 5% @ 5.0", - "rate": 5.0 - }) - si.submit() - - si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test UAE Customer', - currency = 'AED', - warehouse = 'Finished Goods - _TCUV', - debit_to = 'Debtors - _TCUV', - income_account = 'Sales - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - cost_center = 'Main - _TCUV', - sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", - item = "_Test UAE VAT Zero Rated Item", - do_not_save=1 - ) - si.vat_emirate = 'Sharjah' - si.submit() - - si = create_sales_invoice(company="_Test Company UAE VAT", - customer = '_Test UAE Customer', - currency = 'AED', - warehouse = 'Finished Goods - _TCUV', - debit_to = 'Debtors - _TCUV', - income_account = 'Sales - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - cost_center = 'Main - _TCUV', - sales_taxes_and_charges_template = "UAE VAT 5% - _TCUV", - item = "_Test UAE VAT Exempt Item", - do_not_save=1 - ) - si.vat_emirate = 'Sharjah' - si.submit() + #Sales Invoice with standard rated expense in Dubai + make_sales_invoices_wrapper('Dubai', uae_item) + #Sales Invoice with standard rated expense in Sharjah + make_sales_invoices_wrapper('Sharjah', uae_item) + #Sales Invoice with Tourist Tax Return + make_sales_invoices_wrapper('Dubai', uae_item, True, True) + #Sales Invoice with Exempt Item + make_sales_invoices_wrapper('Sharjah', uae_exempt_item, False) + #Sales Invoice with Zero Rated Item + make_sales_invoices_wrapper('Sharjah', uae_zero_rated_item, False) def create_purchase_invoices(): - pi = make_purchase_invoice( - company="_Test Company UAE VAT", - supplier = '_Test UAE Supplier', - supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', - warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', - currency = 'AED', - cost_center = 'Main - _TCUV', - expense_account = 'Cost of Goods Sold - _TCUV', - item = "_Test UAE VAT Item", - do_not_save=1, - uom = "Nos" - ) + company="_Test Company UAE VAT", + supplier = '_Test UAE Supplier', + supplier_warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', + warehouse = '_Test UAE VAT Supplier Warehouse - _TCUV', + currency = 'AED', + cost_center = 'Main - _TCUV', + expense_account = 'Cost of Goods Sold - _TCUV', + item = "_Test UAE VAT Item", + do_not_save=1, + uom = "Nos" + ) pi.append("taxes", { - "charge_type": "On Net Total", - "account_head": "VAT 5% - _TCUV", - "cost_center": "Main - _TCUV", - "description": "VAT 5% @ 5.0", - "rate": 5.0 - }) + "charge_type": "On Net Total", + "account_head": "VAT 5% - _TCUV", + "cost_center": "Main - _TCUV", + "description": "VAT 5% @ 5.0", + "rate": 5.0 + }) pi.recoverable_standard_rated_expenses = 1 diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index f641e652a6..78dcb367a4 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -54,11 +54,11 @@ def get_chart(emirates, amounts_by_emirate): labels = [] amount = [] vat_amount = [] - for d in emirates: - if d in amounts_by_emirate: - amount.append(amounts_by_emirate[d]["raw_amount"]) - vat_amount.append(amounts_by_emirate[d]["raw_vat_amount"]) - labels.append(d) + for emirate in emirates: + if emirate in amounts_by_emirate: + amount.append(amounts_by_emirate[emirate]["raw_amount"]) + vat_amount.append(amounts_by_emirate[emirate]["raw_vat_amount"]) + labels.append(emirate) datasets = [] datasets.append({'name': _('Amount (AED)'), 'values': amount}) @@ -81,7 +81,8 @@ def append_vat_on_sales(data, filters): emirates, amounts_by_emirate = standard_rated_expenses_emiratewise(data, filters) - append_data(data, '2', _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), + append_data(data, '2', + _('Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme'), frappe.format((-1) * get_tourist_tax_return_total(filters), 'Currency'), frappe.format((-1) * get_tourist_tax_return_tax(filters), 'Currency')) @@ -104,37 +105,26 @@ def standard_rated_expenses_emiratewise(data, filters): total_emiratewise = get_total_emiratewise(filters) emirates = get_emirates() amounts_by_emirate = {} - for d in total_emiratewise: - emirate, amount, vat= d + for emirate, amount, vat in total_emiratewise: amounts_by_emirate[emirate] = { - "legend": emirate, - "raw_amount": amount, - "raw_vat_amount": vat, - "amount": frappe.format(amount, 'Currency'), - "vat_amount": frappe.format(vat, 'Currency'), - } + "legend": emirate, + "raw_amount": amount, + "raw_vat_amount": vat, + "amount": frappe.format(amount, 'Currency'), + "vat_amount": frappe.format(vat, 'Currency'), + } amounts_by_emirate = append_emiratewise_expenses(data, emirates, amounts_by_emirate) - - for d, emirate in enumerate(emirates, 97): - if emirate in amounts_by_emirate: - amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) - amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) - data.append(amounts_by_emirate[emirate]) - else: - append_data(data, _('1{0}').format(chr(d)), - _('Standard rated supplies in {0}').format(emirate), - frappe.format(0, 'Currency'), frappe.format(0, 'Currency')) return emirates, amounts_by_emirate def append_emiratewise_expenses(data, emirates, amounts_by_emirate): """Append emiratewise standard rated expenses and vat.""" - for d, emirate in enumerate(emirates, 97): + for no, emirate in enumerate(emirates, 97): if emirate in amounts_by_emirate: - amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(d)) + amounts_by_emirate[emirate]["no"] = _('1{0}').format(chr(no)) amounts_by_emirate[emirate]["legend"] = _('Standard rated supplies in {0}').format(emirate) data.append(amounts_by_emirate[emirate]) else: - append_data(data, _('1{0}').format(chr(d)), + append_data(data, _('1{0}').format(chr(no)), _('Standard rated supplies in {0}').format(emirate), frappe.format(0, 'Currency'), frappe.format(0, 'Currency')) return amounts_by_emirate @@ -145,11 +135,9 @@ def append_vat_on_expenses(data, filters): append_data(data, '9', _('Standard Rated Expenses'), frappe.format(get_standard_rated_expenses_total(filters), 'Currency'), frappe.format(get_standard_rated_expenses_tax(filters), 'Currency')) - append_data(data, '10', _('Supplies subject to the reverse charge provision'), frappe.format(get_reverse_charge_recoverable_total(filters), 'Currency'), - frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency') -) + frappe.format(get_reverse_charge_recoverable_tax(filters), 'Currency')) def append_data(data, no, legend, amount, vat_amount): """Returns data with appended value.""" @@ -159,7 +147,7 @@ def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" query_filters = get_filters(filters) query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', + return frappe.db.get_all('Sales Invoice', filters = query_filters, fields = ['vat_emirate as emirate','sum(total)', 'sum(total_taxes_and_charges)'], group_by='vat_emirate', @@ -194,26 +182,30 @@ def get_reverse_charge_total(filters): query_filters = get_filters(filters) query_filters['reverse_charge'] = ['=', 'Y'] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', - filters = query_filters, - fields = ['sum(total)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_reverse_charge_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" select sum(debit) from - `tabPurchase Invoice` p inner join `tabGL Entry` gl - on gl.voucher_no = p.name + `tabPurchase Invoice` p inner join `tabGL Entry` gl + on + gl.voucher_no = p.name where - p.reverse_charge = "Y" - and p.docstatus = 1 - and gl.docstatus = 1 - and account in (select account from `tabUAE VAT Account` where parent=%(company)s) - {where_conditions} ; + p.reverse_charge = "Y" + and p.docstatus = 1 + and gl.docstatus = 1 + and account in (select account from `tabUAE VAT Account` where parent=%(company)s) + {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 def get_reverse_charge_recoverable_total(filters): @@ -222,27 +214,33 @@ def get_reverse_charge_recoverable_total(filters): query_filters['reverse_charge'] = ['=', 'Y'] query_filters['recoverable_reverse_charge'] = ['>', '0'] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', - filters = query_filters, - fields = ['sum(total)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_reverse_charge_recoverable_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" conditions = get_conditions_join(filters) return frappe.db.sql(""" - select sum(debit * p.recoverable_reverse_charge / 100) from - `tabPurchase Invoice` p inner join `tabGL Entry` gl - on gl.voucher_no = p.name + select + sum(debit * p.recoverable_reverse_charge / 100) + from + `tabPurchase Invoice` p inner join `tabGL Entry` gl + on + gl.voucher_no = p.name where - p.reverse_charge = "Y" - and p.docstatus = 1 - and p.recoverable_reverse_charge > 0 - and gl.docstatus = 1 - and account in (select account from `tabUAE VAT Account` where parent=%(company)s) - {where_conditions} ; + p.reverse_charge = "Y" + and p.docstatus = 1 + and p.recoverable_reverse_charge > 0 + and gl.docstatus = 1 + and account in (select account from `tabUAE VAT Account` where parent=%(company)s) + {where_conditions} ; """.format(where_conditions=conditions), filters)[0][0] or 0 def get_conditions_join(filters): @@ -251,8 +249,8 @@ def get_conditions_join(filters): for opts in (("company", " and p.company=%(company)s"), ("from_date", " and p.posting_date>=%(from_date)s"), ("to_date", " and p.posting_date<=%(to_date)s")): - if filters.get(opts[0]): - conditions += opts[1] + if filters.get(opts[0]): + conditions += opts[1] return conditions def get_standard_rated_expenses_total(filters): @@ -260,77 +258,102 @@ def get_standard_rated_expenses_total(filters): query_filters = get_filters(filters) query_filters['recoverable_standard_rated_expenses'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', - filters = query_filters, - fields = ['sum(total)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Purchase Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" query_filters = get_filters(filters) query_filters['recoverable_standard_rated_expenses'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Purchase Invoice', - filters = query_filters, - fields = ['sum(recoverable_standard_rated_expenses)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Purchase Invoice', + filters = query_filters, + fields = ['sum(recoverable_standard_rated_expenses)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_tourist_tax_return_total(filters): """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return.""" query_filters = get_filters(filters) query_filters['tourist_tax_return'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', - filters = query_filters, - fields = ['sum(total)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Sales Invoice', + filters = query_filters, + fields = ['sum(total)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_tourist_tax_return_tax(filters): """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return.""" query_filters = get_filters(filters) query_filters['tourist_tax_return'] = ['>', 0] query_filters['docstatus'] = ['=', 1] - return frappe.db.get_list('Sales Invoice', - filters = query_filters, - fields = ['sum(tourist_tax_return)'], - as_list=True, - limit = 1 - )[0][0] or 0 + try: + return frappe.db.get_all('Sales Invoice', + filters = query_filters, + fields = ['sum(tourist_tax_return)'], + as_list=True, + limit = 1 + )[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_zero_rated_total(filters): """Returns the sum of each Sales Invoice Item Amount which is zero rated.""" conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(i.base_amount) as total from - `tabSales Invoice Item` i inner join `tabSales Invoice` s - on i.parent = s.name - where s.docstatus = 1 and i.is_zero_rated = 1 - {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 + try: + return frappe.db.sql(""" + select + sum(i.base_amount) as total + from + `tabSales Invoice Item` i inner join `tabSales Invoice` s + on + i.parent = s.name + where + s.docstatus = 1 and i.is_zero_rated = 1 + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_exempt_total(filters): """Returns the sum of each Sales Invoice Item Amount which is Vat Exempt.""" conditions = get_conditions(filters) - return frappe.db.sql(""" - select sum(i.base_amount) as total from - `tabSales Invoice Item` i inner join `tabSales Invoice` s - on i.parent = s.name - where s.docstatus = 1 and i.is_exempt = 1 - {where_conditions} ; - """.format(where_conditions=conditions), filters)[0][0] or 0 - + try: + return frappe.db.sql(""" + select + sum(i.base_amount) as total + from + `tabSales Invoice Item` i inner join `tabSales Invoice` s + on + i.parent = s.name + where + s.docstatus = 1 and i.is_exempt = 1 + {where_conditions} ; + """.format(where_conditions=conditions), filters)[0][0] or 0 + except (IndexError, TypeError): + return 0 def get_conditions(filters): """The conditions to be used to filter data to calculate the total sale.""" conditions = "" for opts in (("company", " and company=%(company)s"), ("from_date", " and posting_date>=%(from_date)s"), ("to_date", " and posting_date<=%(to_date)s")): - if filters.get(opts[0]): - conditions += opts[1] + if filters.get(opts[0]): + conditions += opts[1] return conditions diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 7430db4fce..872839495a 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -35,7 +35,12 @@ def get_account_currency(account): if not account: return def generator(): - account_currency, company = frappe.get_cached_value("Account", account, ["account_currency", "company"]) + account_currency, company = frappe.get_cached_value( + "Account", + account, + ["account_currency", + "company"] + ) if not account_currency: account_currency = frappe.get_cached_value('Company', company, "default_currency") @@ -53,8 +58,8 @@ def get_tax_accounts(company): if not tax_accounts_list and not frappe.flags.in_test: frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company)) - for d in tax_accounts_list: - for key, name in d.items(): + for tax_account in tax_accounts_list: + for _, name in tax_account.items(): tax_accounts_dict[name] = name return tax_accounts_dict @@ -131,8 +136,7 @@ def make_gl_entry(tax, gl_entries, doc, tax_accounts): if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in tax_accounts: account_currency = get_account_currency(tax.account_head) - gl_entries.append(doc.get_gl_dict( - { + gl_entries.append(doc.get_gl_dict({ "account": tax.account_head, "cost_center": tax.cost_center, "posting_date": doc.posting_date, @@ -141,8 +145,8 @@ def make_gl_entry(tax, gl_entries, doc, tax_accounts): dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \ if account_currency==doc.company_currency \ else tax.tax_amount_after_discount_amount - }, account_currency, item=tax) - ) + }, account_currency, item=tax + )) return gl_entries @@ -152,4 +156,6 @@ def validate_returns(doc, method): if country != 'United Arab Emirates': return if doc.reverse_charge == 'Y' and flt(doc.recoverable_standard_rated_expenses) != 0: - frappe.throw(_("Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y")) \ No newline at end of file + frappe.throw(_( + "Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y" + )) From 7a034595fe7c2f60090e9bbdc185a98ecd0f4f38 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 2 Nov 2020 13:12:27 +0530 Subject: [PATCH 071/283] fix(UAE VAT 201): Remove Chart --- .../report/uae_vat_201/uae_vat_201.py | 29 +------------------ .../regional/united_arab_emirates/utils.py | 2 +- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 78dcb367a4..c151c622a6 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -8,8 +8,7 @@ from frappe import _ def execute(filters=None): columns = get_columns() data, emirates, amounts_by_emirate = get_data(filters) - chart = get_chart(emirates, amounts_by_emirate) - return columns, data, None, chart + return columns, data def get_columns(): """Creates a list of dictionaries that are used to generate column headers of the data table.""" @@ -49,32 +48,6 @@ def get_data(filters = None): append_vat_on_expenses(data, filters) return data, emirates, amounts_by_emirate -def get_chart(emirates, amounts_by_emirate): - """Returns chart data.""" - labels = [] - amount = [] - vat_amount = [] - for emirate in emirates: - if emirate in amounts_by_emirate: - amount.append(amounts_by_emirate[emirate]["raw_amount"]) - vat_amount.append(amounts_by_emirate[emirate]["raw_vat_amount"]) - labels.append(emirate) - - datasets = [] - datasets.append({'name': _('Amount (AED)'), 'values': amount}) - datasets.append({'name': _('Vat Amount (AED)'), 'values': vat_amount}) - - chart = { - "type": "bar", - "fieldtype": "Currency", - "data": { - 'labels': labels, - 'datasets': datasets - } - } - - return chart - def append_vat_on_sales(data, filters): """Appends Sales and All Other Outputs.""" append_data(data, '', _('VAT on Sales and All Other Outputs'), '', '') diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 872839495a..7d5fd6ecf8 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -59,7 +59,7 @@ def get_tax_accounts(company): if not tax_accounts_list and not frappe.flags.in_test: frappe.throw(_('Please set Vat Accounts for Company: "{0}" in UAE VAT Settings').format(company)) for tax_account in tax_accounts_list: - for _, name in tax_account.items(): + for account, name in tax_account.items(): tax_accounts_dict[name] = name return tax_accounts_dict From d6596a169cd2de4a4dfc74cf68d454cec290a30d Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 2 Nov 2020 15:07:48 +0530 Subject: [PATCH 072/283] fix: Billing % Logic and Map Pending Qty only in PR and DN - Billing % should consider unreturned amount as total - While mapping to return doc, map unreturned amount - Added field Received Qty in Stock UOM, to tally against Returned Qty in PR - PR billing percentage updation custom function - In patch set received qty in stock uom first, then update returned qty and billing --- .../purchase_invoice/purchase_invoice.py | 4 +- erpnext/controllers/buying_controller.py | 4 ++ .../controllers/sales_and_purchase_return.py | 58 ++++++++++++++++--- erpnext/controllers/stock_controller.py | 6 +- erpnext/patches.txt | 2 +- .../v13_0/update_returned_qty_in_pr_dn.py | 7 +++ erpnext/public/js/controllers/buying.js | 1 + .../purchase_receipt/purchase_receipt.py | 40 ++++++++++++- .../purchase_receipt_item.json | 9 ++- 9 files changed, 117 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 91c4dfb587..014f05c4c1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1032,7 +1032,9 @@ class PurchaseInvoice(BuyingController): updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified) for pr in set(updated_pr): - frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified) + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage + pr_doc = frappe.get_doc("Purchase Receipt", pr) + update_billing_percentage(pr_doc, update_modified=update_modified) def on_recurring(self, reference_doc, auto_repeat_doc): self.due_date = None diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index f376836f7b..af2474d3de 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -497,6 +497,10 @@ class BuyingController(StockController): frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx)) d.stock_qty = flt(d.qty) * flt(d.conversion_factor) + if self.doctype=="Purchase Receipt" and d.meta.get_field("received_stock_qty"): + # Set Received Qty in Stock UOM + d.received_stock_qty = flt(d.received_qty) * flt(d.conversion_factor, d.precision("conversion_factor")) + def validate_purchase_return(self): for d in self.get("items"): if self.is_return and flt(d.rejected_qty) != 0: diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index b4da5fa3e7..e11289d79e 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -203,6 +203,41 @@ def get_already_returned_items(doc): return items +def get_returned_qty_map_for_row(row_name, doctype): + child_doctype = doctype + " Item" + reference_field = frappe.scrub(child_doctype) if doctype == "Purchase Receipt" else "dn_detail" + reference_field = "child." + reference_field + columns = "" + + if doctype == "Purchase Receipt": + columns += ", sum(abs(child.rejected_qty)) as rejected_qty, \ + sum(abs(child.received_qty)) as received_qty, \ + sum(abs(child.received_stock_qty)) as received_stock_qty" + + data = frappe.db.sql(""" + select + sum(abs(child.qty)) as qty, + sum(abs(child.stock_qty)) as stock_qty, + %(columns)s + from + `tab{0}` child, `tab{1}` parent + where + child.parent = parent.name + and parent.docstatus = 1 + and parent.is_return = 1 + and {2} = %(row_name)s + """.format(child_doctype, doctype, reference_field), + { + "row_name": row_name, + "columns": columns, + "child_doctype": child_doctype, + "doctype": doctype, + "reference_field": reference_field + }, + as_dict=1) + + return data[0] + def make_return_doc(doctype, source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc company = frappe.db.get_value("Delivery Note", source_name, "company") @@ -262,20 +297,25 @@ def make_return_doc(doctype, source_name, target_doc=None): doc.run_method("calculate_taxes_and_totals") def update_item(source_doc, target_doc, source_parent): - target_doc.qty = -1* source_doc.qty + target_doc.qty = -1 * source_doc.qty + if doctype == "Purchase Receipt": - target_doc.received_qty = -1* source_doc.received_qty - target_doc.rejected_qty = -1* source_doc.rejected_qty - target_doc.qty = -1* source_doc.qty - target_doc.stock_qty = -1 * source_doc.stock_qty + returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype) + target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0)) + target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0)) + target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0)) + + target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0)) + target_doc.received_stock_qty = -1 * flt(source_doc.received_stock_qty - (returned_qty_map.get('received_stock_qty') or 0)) + target_doc.purchase_order = source_doc.purchase_order target_doc.purchase_order_item = source_doc.purchase_order_item target_doc.rejected_warehouse = source_doc.rejected_warehouse target_doc.purchase_receipt_item = source_doc.name elif doctype == "Purchase Invoice": - target_doc.received_qty = -1* source_doc.received_qty - target_doc.rejected_qty = -1* source_doc.rejected_qty + target_doc.received_qty = -1 * source_doc.received_qty + target_doc.rejected_qty = -1 * source_doc.rejected_qty target_doc.qty = -1* source_doc.qty target_doc.stock_qty = -1 * source_doc.stock_qty target_doc.purchase_order = source_doc.purchase_order @@ -286,6 +326,10 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.purchase_invoice_item = source_doc.name elif doctype == "Delivery Note": + returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype) + target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0)) + target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0)) + target_doc.against_sales_order = source_doc.against_sales_order target_doc.against_sales_invoice = source_doc.against_sales_invoice target_doc.so_detail = source_doc.so_detail diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f743d707f7..196279fa5c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -338,11 +338,15 @@ class StockController(AccountsController): validate_warehouse_company(w, self.company) def update_billing_percentage(self, update_modified=True): + target_ref_field = "amount" + if self.doctype == "Delivery Note": + target_ref_field = "amount - (returned_qty * rate)" + self._update_percent_field({ "target_dt": self.doctype + " Item", "target_parent_dt": self.doctype, "target_parent_field": "per_billed", - "target_ref_field": "amount", + "target_ref_field": target_ref_field, "target_field": "billed_amt", "name": self.name, }, update_modified) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6dfa085b58..dc7e99bcde 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -732,4 +732,4 @@ erpnext.patches.v13_0.set_youtube_video_id erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail -erpnext.patches.v13_0.update_returned_qty_in_pr_dn +erpnext.patches.v13_0.update_returned_qty_in_pr_dn #12am \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py index a13640e1b0..7f42cd92e3 100644 --- a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py +++ b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py @@ -15,6 +15,13 @@ def execute(): # Update original receipt/delivery document from return return_doc = frappe.get_cached_doc(doctype, return_doc.name) return_doc.update_prevdoc_status() + return_against = frappe.get_doc(doctype, return_doc.return_against) + return_against.update_billing_status() + + # Set received qty in stock uom in PR, as returned qty is checked against it + frappe.db.sql(""" update `tabPurchase Receipt Item` + set received_stock_qty = received_qty * conversion_factor + where docstatus = 1 """) for doctype in ('Purchase Receipt', 'Delivery Note'): update_from_return_docs(doctype) \ No newline at end of file diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cb76c87b62..11f70f7f59 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -189,6 +189,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ frappe.model.round_floats_in(item, ["qty", "received_qty"]); item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item)); + item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty); } this._super(doc, cdt, cdn); diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 551f3777a5..be3ff5e5c2 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -77,8 +77,8 @@ class PurchaseReceipt(BuyingController): 'target_field': 'returned_qty', 'target_parent_dt': 'Purchase Receipt', 'target_parent_field': 'per_returned', - 'target_ref_field': 'stock_qty', - 'source_field': '-1 * stock_qty', + 'target_ref_field': 'received_stock_qty', + 'source_field': '-1 * received_stock_qty', 'percent_join_field_parent': 'return_against' } ]) @@ -503,7 +503,7 @@ class PurchaseReceipt(BuyingController): for pr in set(updated_pr): pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr) - pr_doc.update_billing_percentage(update_modified=update_modified) + update_billing_percentage(pr_doc, update_modified=update_modified) self.load_from_db() @@ -543,6 +543,39 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True): return updated_pr +def update_billing_percentage(pr_doc, update_modified=True): + # Update Billing % based on pending accepted qty + total_amount, total_billed_amount = 0, 0 + for item in pr_doc.items: + returned_qty = frappe.db.sql(""" + select sum(abs(child.qty)) as qty + from + `tabPurchase Receipt Item` child, + `tabPurchase Receipt` parent + where + child.parent = parent.name + and parent.docstatus = 1 + and parent.is_return = 1 + and child.purchase_receipt_item = %(row_name)s + """, {"row_name": item.name}) + returned_qty = returned_qty[0][0] if returned_qty else 0 + + returned_amount = flt(returned_qty) * flt(item.rate) + pending_amount = flt(item.amount) - returned_amount + total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt + + total_amount += total_billable_amount + total_billed_amount += flt(item.billed_amt) + + print(total_billed_amount, total_amount) + percent_billed = round(100 * (total_billed_amount / total_amount), 6) + pr_doc.db_set("per_billed", percent_billed) + pr_doc.load_from_db() + + if update_modified: + pr_doc.set_status(update=True) + pr_doc.notify_update() + @frappe.whitelist() def make_purchase_invoice(source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc @@ -562,6 +595,7 @@ def make_purchase_invoice(source_name, target_doc=None): def update_item(source_doc, target_doc, source_parent): target_doc.qty, returned_qty = get_pending_qty(source_doc) + target_doc.stock_qty = flt(target_doc.qty) * flt(target_doc.conversion_factor, target_doc.precision("conversion_factor")) returned_qty_map[source_doc.name] = returned_qty def get_pending_qty(item_row): diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 20ae56feeb..84c64aa8f8 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -31,6 +31,7 @@ "retain_sample", "sample_quantity", "tracking_section", + "received_stock_qty", "stock_qty", "col_break_tracking_section", "returned_qty", @@ -854,12 +855,18 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "received_stock_qty", + "fieldtype": "Float", + "label": "Received Qty in Stock UOM", + "print_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-09-09 13:39:46.452817", + "modified": "2020-11-02 10:00:38.204294", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 3991b84b2bf26d30402d33ef479ba8af5843c220 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 2 Nov 2020 15:23:41 +0530 Subject: [PATCH 073/283] chore: Avoid multiline string in Translation & remove print statement --- erpnext/controllers/stock_controller.py | 4 ++-- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 196279fa5c..4436ab07e5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -229,8 +229,8 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ - Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense Account in the Items table") + .format(item.idx, frappe.bold(item.item_code)), title=_("Expense Account Missing")) else: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index be3ff5e5c2..c37740cc7d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -567,7 +567,6 @@ def update_billing_percentage(pr_doc, update_modified=True): total_amount += total_billable_amount total_billed_amount += flt(item.billed_amt) - print(total_billed_amount, total_amount) percent_billed = round(100 * (total_billed_amount / total_amount), 6) pr_doc.db_set("per_billed", percent_billed) pr_doc.load_from_db() From a2ba8ea74a39403d9cd2fd2cc2af895059d9cdf8 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Tue, 3 Nov 2020 10:57:52 +0800 Subject: [PATCH 074/283] style: Clean up code formatting --- .../stock/doctype/item_price/item_price.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 54f534f3d1..bed5ea9ab6 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -46,12 +46,7 @@ class ItemPrice(Document): self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code,["item_name", "description"]) def check_duplicates(self): - conditions = """ - where - item_code = %(item_code)s - and price_list = %(price_list)s - and name != %(name)s - """ + conditions = """where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s""" for field in [ "uom", @@ -59,23 +54,18 @@ class ItemPrice(Document): "valid_upto", "packing_unit", "customer", - "supplier", - ]: + "supplier",]: if self.get(field): conditions += " and {0} = %({0})s ".format(field) else: conditions += "and (isnull({0}) or {0} = '')".format(field) - price_list_rate = frappe.db.sql( - """ + price_list_rate = frappe.db.sql(""" select price_list_rate from `tabItem Price` {conditions} - """.format( - conditions=conditions - ), - self.as_dict(), - ) + """.format(conditions=conditions), + self.as_dict(),) if price_list_rate: frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,) From f21e3fbf04c157ff74d40b88cb7bd4bc7d7578ca Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 3 Nov 2020 12:01:56 +0530 Subject: [PATCH 075/283] chore: Tests - Added test for mapping secnd return doc - Added test for billing % of partially returned doc - Handled PR with 0 billing amount --- .../delivery_note/test_delivery_note.py | 26 ++++++++++++ .../purchase_receipt/purchase_receipt.py | 7 +++- .../purchase_receipt/test_purchase_receipt.py | 41 +++++++++++++++++-- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 9f273d7959..fa07a2510c 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -255,6 +255,32 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(dn.items[0].returned_qty, 2) self.assertEqual(dn.per_returned, 40) + from erpnext.controllers.sales_and_purchase_return import make_return_doc + return_dn_2 = make_return_doc("Delivery Note", dn.name) + + # Check if unreturned amount is mapped in 2nd return + self.assertEqual(return_dn_2.items[0].qty, -3) + + si = make_sales_invoice(dn.name) + si.submit() + + self.assertEqual(si.items[0].qty, 3) + + dn.load_from_db() + # DN should be completed on billing all unreturned amount + self.assertEqual(dn.items[0].billed_amt, 1500) + self.assertEqual(dn.per_billed, 100) + self.assertEqual(dn.status, 'Completed') + + si.load_from_db() + si.cancel() + + dn.load_from_db() + self.assertEqual(dn.per_billed, 0) + + dn1.cancel() + dn.cancel() + def test_sales_return_for_non_bundled_items_full(self): from erpnext.stock.doctype.item.test_item import make_item diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index c37740cc7d..1852985b1d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -513,7 +513,7 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True): where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""", po_detail) billed_against_po = billed_against_po and billed_against_po[0][0] or 0 - # Get all Delivery Note Item rows against the Sales Order Item row + # Get all Purchase Receipt Item rows against the Purchase Order Item row pr_details = frappe.db.sql("""select pr_item.name, pr_item.amount, pr_item.parent from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr where pr.name=pr_item.parent and pr_item.purchase_order_item=%s @@ -544,6 +544,9 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True): return updated_pr def update_billing_percentage(pr_doc, update_modified=True): + # Reload as billed amount was set in db directly + pr_doc.load_from_db() + # Update Billing % based on pending accepted qty total_amount, total_billed_amount = 0, 0 for item in pr_doc.items: @@ -567,7 +570,7 @@ def update_billing_percentage(pr_doc, update_modified=True): total_amount += total_billable_amount total_billed_amount += flt(item.billed_amt) - percent_billed = round(100 * (total_billed_amount / total_amount), 6) + percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) pr_doc.db_set("per_billed", percent_billed) pr_doc.load_from_db() diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index aef5bf3959..c23d6c2b53 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -100,7 +100,10 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no})) def test_purchase_receipt_gl_entry(self): - pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True) + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", + warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", + get_multiple_items = True, get_taxes_and_charges = True) + self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1) gl_entries = get_gl_entries("Purchase Receipt", pr.name) @@ -245,10 +248,12 @@ class TestPurchaseReceipt(unittest.TestCase): pr.get("items")[0].rejected_warehouse) def test_purchase_return_partial(self): + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", + warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") - pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") - - return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-2, do_not_submit=1) + return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", + warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", + is_return=1, return_against=pr.name, qty=-2, do_not_submit=1) return_pr.items[0].purchase_receipt_item = pr.items[0].name return_pr.submit() @@ -283,6 +288,33 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(pr.items[0].returned_qty, 2) self.assertEqual(pr.per_returned, 40) + from erpnext.controllers.sales_and_purchase_return import make_return_doc + return_pr_2 = make_return_doc("Purchase Receipt", pr.name) + + # Check if unreturned amount is mapped in 2nd return + self.assertEqual(return_pr_2.items[0].qty, -3) + + # Make PI against unreturned amount + pi = make_purchase_invoice(pr.name) + pi.submit() + + self.assertEqual(pi.items[0].qty, 3) + + pr.load_from_db() + # PR should be completed on billing all unreturned amount + self.assertEqual(pr.items[0].billed_amt, 150) + self.assertEqual(pr.per_billed, 100) + self.assertEqual(pr.status, 'Completed') + + pi.load_from_db() + pi.cancel() + + pr.load_from_db() + self.assertEqual(pr.per_billed, 0) + + return_pr.cancel() + pr.cancel() + def test_purchase_return_full(self): pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") @@ -406,6 +438,7 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(pr1.per_billed, 100) self.assertEqual(pr1.status, "Completed") + pr2.load_from_db() self.assertEqual(pr2.get("items")[0].billed_amt, 2000) self.assertEqual(pr2.per_billed, 80) self.assertEqual(pr2.status, "To Bill") From 53b1a9a40bb792614324316f8a933c72a92beac3 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 3 Nov 2020 15:45:25 +0530 Subject: [PATCH 076/283] chore: Add Test for missing debit account --- .../test_opening_invoice_creation_tool.py | 53 +++++++++++++++++-- erpnext/controllers/accounts_controller.py | 6 ++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py index 54229f5247..329d84bdb7 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py @@ -7,16 +7,18 @@ import frappe import unittest test_dependencies = ["Customer", "Supplier"] +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account class TestOpeningInvoiceCreationTool(unittest.TestCase): - def make_invoices(self, invoice_type="Sales"): + def make_invoices(self, invoice_type="Sales", company=None): doc = frappe.get_single("Opening Invoice Creation Tool") - args = get_opening_invoice_creation_dict(invoice_type=invoice_type) + args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company) doc.update(args) return doc.make_invoices() def test_opening_sales_invoice_creation(self): + property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check") invoices = self.make_invoices() self.assertEqual(len(invoices), 2) @@ -27,6 +29,13 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase): } self.check_expected_values(invoices, expected_value) + si = frappe.get_doc("Sales Invoice", invoices[0]) + + # Check if update stock is not enabled + self.assertEqual(si.update_stock, 0) + + property_setter.delete() + def check_expected_values(self, invoices, expected_value, invoice_type="Sales"): doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice" @@ -46,6 +55,32 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase): } self.check_expected_values(invoices, expected_value, "Purchase") + def test_opening_sales_invoice_creation_with_missing_debit_account(self): + company = make_company() + old_default_receivable_account = frappe.db.get_value("Company", company.name, "default_receivable_account") + frappe.db.set_value("Company", company.name, "default_receivable_account", "") + + if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"): + cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company", + "is_group": 1, "company": "_Test Opening Invoice Company"}) + cc.insert(ignore_mandatory=True) + cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0, + "company": "_Test Opening Invoice Company", "parent_cost_center": cc.name}) + cc2.insert() + + frappe.db.set_value("Company", company.name, "cost_center", "Main - _TOIC") + + self.make_invoices(company="_Test Opening Invoice Company") + + # Check if missing debit account error raised + error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]}) + self.assertTrue(error_log) + + # teardown + frappe.db.set_value("Company", company.name, "default_receivable_account", old_default_receivable_account) + company.delete() + frappe.get_doc("Error Log", error_log).delete() + def get_opening_invoice_creation_dict(**args): party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier" company = args.get("company", "_Test Company") @@ -76,4 +111,16 @@ def get_opening_invoice_creation_dict(**args): }) invoice_dict.update(args) - return invoice_dict \ No newline at end of file + return invoice_dict + +def make_company(): + if frappe.db.exists("Company", "_Test Opening Invoice Company"): + return frappe.get_doc("Company", "_Test Opening Invoice Company") + + company = frappe.new_doc("Company") + company.company_name = "_Test Opening Invoice Company" + company.abbr = "_TOIC" + company.default_currency = "INR" + company.country = "India" + company.insert() + return company \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 28c73a39e9..93a79ec934 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -23,6 +23,8 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map from erpnext.stock.doctype.packed_item.packed_item import make_packing_list +class AccountMissingError(frappe.ValidationError): pass + force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") class AccountsController(TransactionBase): @@ -736,7 +738,7 @@ class AccountsController(TransactionBase): return self._abbr def raise_missing_debit_credit_account_error(self, party_type, party): - """Raise an error if debit to/credit to account does not exist""" + """Raise an error if debit to/credit to account does not exist.""" db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To") rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable" @@ -748,7 +750,7 @@ class AccountsController(TransactionBase): message += "
  • " + _("'Account' in the Accounting section of Customer {0}").format(link_to_party) + "
  • " message += "
  • " + _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company) + "
" - frappe.throw(message, title=_("Account Missing")) + frappe.throw(message, title=_("Account Missing"), exc=AccountMissingError) def validate_party(self): party_type, party = self.get_party() From 7837161a3fbf52d384896b146ae1491447045078 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 3 Nov 2020 22:09:42 +0530 Subject: [PATCH 077/283] fix: Sider --- .../stock/doctype/purchase_receipt/test_purchase_receipt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c23d6c2b53..722b2c9aea 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -316,9 +316,11 @@ class TestPurchaseReceipt(unittest.TestCase): pr.cancel() def test_purchase_return_full(self): - pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", + supplier_warehouse = "Work in Progress - TCP1") - return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-5, do_not_submit=1) + return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", + supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-5, do_not_submit=1) return_pr.items[0].purchase_receipt_item = pr.items[0].name return_pr.submit() From f2c895ddb5b9d7b5b8f07e1d590e07ff5a745b7c Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 4 Nov 2020 10:31:37 +0530 Subject: [PATCH 078/283] feat(uae vat 201): do not take zero and nil rated items in standard rated expense emiratewise --- .../report/uae_vat_201/test_uae_vat_201.py | 2 +- .../report/uae_vat_201/uae_vat_201.py | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py index b0a21de613..daa69768c5 100644 --- a/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/test_uae_vat_201.py @@ -53,7 +53,7 @@ class TestUaeVat201(TestCase): "raw_amount": amount, "raw_vat_amount": vat, } - self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],300) + self.assertEqual(amounts_by_emirate["Sharjah"]["raw_amount"],100) self.assertEqual(amounts_by_emirate["Sharjah"]["raw_vat_amount"],5) self.assertEqual(amounts_by_emirate["Dubai"]["raw_amount"],200) self.assertEqual(amounts_by_emirate["Dubai"]["raw_vat_amount"],10) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index c151c622a6..f2d5a5e113 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -118,14 +118,23 @@ def append_data(data, no, legend, amount, vat_amount): def get_total_emiratewise(filters): """Returns Emiratewise Amount and Taxes.""" - query_filters = get_filters(filters) - query_filters['docstatus'] = ['=', 1] - return frappe.db.get_all('Sales Invoice', - filters = query_filters, - fields = ['vat_emirate as emirate','sum(total)', 'sum(total_taxes_and_charges)'], - group_by='vat_emirate', - as_list=True - ) + conditions = get_conditions(filters) + try: + return frappe.db.sql(""" + select + s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges) + from + `tabSales Invoice Item` i inner join `tabSales Invoice` s + on + i.parent = s.name + where + s.docstatus = 1 and i.is_exempt != 1 and i.is_zero_rated != 1 + {where_conditions} + group by + s.vat_emirate; + """.format(where_conditions=conditions), filters) + except (IndexError, TypeError): + return 0 def get_emirates(): """Returns a List of emirates in the order that they are to be displayed.""" From bed4e85aec2b8475ab817916f0d357c6e977fe55 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Wed, 4 Nov 2020 10:44:22 +0530 Subject: [PATCH 079/283] fix(uae vat 201): date filters were not working --- .../report/uae_vat_201/uae_vat_201.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index f2d5a5e113..de727d2ecd 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -150,20 +150,20 @@ def get_emirates(): def get_filters(filters): """The conditions to be used to filter data to calculate the total sale.""" - query_filters = {} + query_filters = [] if filters.get("company"): - query_filters["company"] = ['=', filters['company']] + query_filters.append(["company", '=', filters['company']]) if filters.get("from_date"): - query_filters["posting_date"] = ['>=', filters['from_date']] + query_filters.append(["posting_date", '>=', filters['from_date']]) if filters.get("from_date"): - query_filters["posting_date"] = ['<=', filters['to_date']] + query_filters.append(["posting_date", '<=', filters['to_date']]) return query_filters def get_reverse_charge_total(filters): """Returns the sum of the total of each Purchase invoice made.""" query_filters = get_filters(filters) - query_filters['reverse_charge'] = ['=', 'Y'] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['reverse_charge', '=', 'Y']) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Purchase Invoice', filters = query_filters, @@ -193,9 +193,9 @@ def get_reverse_charge_tax(filters): def get_reverse_charge_recoverable_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" query_filters = get_filters(filters) - query_filters['reverse_charge'] = ['=', 'Y'] - query_filters['recoverable_reverse_charge'] = ['>', '0'] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['reverse_charge', '=', 'Y']) + query_filters.append(['recoverable_reverse_charge', '>', '0']) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Purchase Invoice', filters = query_filters, @@ -238,8 +238,8 @@ def get_conditions_join(filters): def get_standard_rated_expenses_total(filters): """Returns the sum of the total of each Purchase invoice made with recoverable reverse charge.""" query_filters = get_filters(filters) - query_filters['recoverable_standard_rated_expenses'] = ['>', 0] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['recoverable_standard_rated_expenses', '>', 0]) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Purchase Invoice', filters = query_filters, @@ -253,8 +253,8 @@ def get_standard_rated_expenses_total(filters): def get_standard_rated_expenses_tax(filters): """Returns the sum of the tax of each Purchase invoice made.""" query_filters = get_filters(filters) - query_filters['recoverable_standard_rated_expenses'] = ['>', 0] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['recoverable_standard_rated_expenses', '>', 0]) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Purchase Invoice', filters = query_filters, @@ -268,8 +268,8 @@ def get_standard_rated_expenses_tax(filters): def get_tourist_tax_return_total(filters): """Returns the sum of the total of each Sales invoice with non zero tourist_tax_return.""" query_filters = get_filters(filters) - query_filters['tourist_tax_return'] = ['>', 0] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['tourist_tax_return', '>', 0]) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Sales Invoice', filters = query_filters, @@ -283,8 +283,8 @@ def get_tourist_tax_return_total(filters): def get_tourist_tax_return_tax(filters): """Returns the sum of the tax of each Sales invoice with non zero tourist_tax_return.""" query_filters = get_filters(filters) - query_filters['tourist_tax_return'] = ['>', 0] - query_filters['docstatus'] = ['=', 1] + query_filters.append(['tourist_tax_return', '>', 0]) + query_filters.append(['docstatus', '=', 1]) try: return frappe.db.get_all('Sales Invoice', filters = query_filters, @@ -300,7 +300,7 @@ def get_zero_rated_total(filters): conditions = get_conditions(filters) try: return frappe.db.sql(""" - select + select sum(i.base_amount) as total from `tabSales Invoice Item` i inner join `tabSales Invoice` s From aa08fb971659ffd3e801db2954d06277238a6c25 Mon Sep 17 00:00:00 2001 From: igormbq Date: Wed, 4 Nov 2020 11:40:57 -0300 Subject: [PATCH 080/283] Add location on Asset to use make_demo --- erpnext/demo/data/asset.json | 21 ++++++++++++++------- erpnext/demo/data/location.json | 22 ++++++++++++++++++++++ erpnext/demo/setup/manufacture.py | 1 + erpnext/demo/user/stock.py | 2 +- 4 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 erpnext/demo/data/location.json diff --git a/erpnext/demo/data/asset.json b/erpnext/demo/data/asset.json index 23029ca5e3..44db2ae9e1 100644 --- a/erpnext/demo/data/asset.json +++ b/erpnext/demo/data/asset.json @@ -4,48 +4,55 @@ "item_code": "Computer", "gross_purchase_amount": 100000, "asset_owner": "Company", - "available_for_use_date": "2017-01-02" + "available_for_use_date": "2017-01-02", + "location": "Main Location" }, { "asset_name": "Macbook Air - 1", "item_code": "Computer", "gross_purchase_amount": 60000, "asset_owner": "Company", - "available_for_use_date": "2017-10-02" + "available_for_use_date": "2017-10-02", + "location": "Avg Location" }, { "asset_name": "Conferrence Table", "item_code": "Table", "gross_purchase_amount": 30000, "asset_owner": "Company", - "available_for_use_date": "2018-10-02" + "available_for_use_date": "2018-10-02", + "location": "Zany Location" }, { "asset_name": "Lunch Table", "item_code": "Table", "gross_purchase_amount": 20000, "asset_owner": "Company", - "available_for_use_date": "2018-06-02" + "available_for_use_date": "2018-06-02", + "location": "Fletcher Location" }, { "asset_name": "ERPNext", "item_code": "ERP", "gross_purchase_amount": 100000, "asset_owner": "Company", - "available_for_use_date": "2018-09-02" + "available_for_use_date": "2018-09-02", + "location":"Main Location" }, { "asset_name": "Chair 1", "item_code": "Chair", "gross_purchase_amount": 10000, "asset_owner": "Company", - "available_for_use_date": "2018-07-02" + "available_for_use_date": "2018-07-02", + "location": "Zany Location" }, { "asset_name": "Chair 2", "item_code": "Chair", "gross_purchase_amount": 10000, "asset_owner": "Company", - "available_for_use_date": "2018-07-02" + "available_for_use_date": "2018-07-02", + "location": "Avg Location" } ] diff --git a/erpnext/demo/data/location.json b/erpnext/demo/data/location.json new file mode 100644 index 0000000000..b521aa08c4 --- /dev/null +++ b/erpnext/demo/data/location.json @@ -0,0 +1,22 @@ +[ + { + "location_name": "Main Location", + "latitude": 40.0, + "longitude": 20.0 + }, + { + "location_name": "Avg Location", + "latitude": 63.0, + "longitude": 99.3 + }, + { + "location_name": "Zany Location", + "latitude": 47.5, + "longitude": 10.0 + }, + { + "location_name": "Fletcher Location", + "latitude": 100.90, + "longitude": 80 + } +] \ No newline at end of file diff --git a/erpnext/demo/setup/manufacture.py b/erpnext/demo/setup/manufacture.py index d3846369cd..7d6b5012ea 100644 --- a/erpnext/demo/setup/manufacture.py +++ b/erpnext/demo/setup/manufacture.py @@ -9,6 +9,7 @@ from erpnext.demo.domains import data from six import iteritems def setup_data(): + import_json("Location") import_json("Asset Category") setup_item() setup_workstation() diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py index f95a6b8331..d44da7d127 100644 --- a/erpnext/demo/user/stock.py +++ b/erpnext/demo/user/stock.py @@ -79,7 +79,7 @@ def make_stock_reconciliation(): if item.qty: item.qty = item.qty - round(random.randint(1, item.qty)) try: - stock_reco.insert(ignore_permissions=True) + stock_reco.insert(ignore_permissions=True, ignore_mandatory=True) stock_reco.submit() frappe.db.commit() except OpeningEntryAccountError: From 403822afb315b80c1c48c05ccf9749ba08ff73ae Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 5 Nov 2020 16:24:33 +0530 Subject: [PATCH 081/283] fix: correcting description field in taxes and charges for accounts that have account number + account name --- erpnext/public/js/controllers/accounts.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 6e97d811fc..b1ffcfb6ad 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -146,18 +146,21 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) { if(!d.charge_type && d.account_head){ frappe.msgprint(__("Please select Charge Type first")); frappe.model.set_value(cdt, cdn, "account_head", ""); - } else if(d.account_head && d.charge_type!=="Actual") { + } else if(d.account_head) { frappe.call({ type:"GET", method: "erpnext.controllers.accounts_controller.get_tax_rate", args: {"account_head":d.account_head}, callback: function(r) { - frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0); - frappe.model.set_value(cdt, cdn, "description", r.message.account_name); + if(d.charge_type!=="Actual"){ + frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0); + frappe.model.set_value(cdt, cdn, "description", r.message.account_name); + } + else if(d.charge_type == 'Actual'){ + frappe.model.set_value(cdt, cdn, "description", r.message.account_name); + } } }) - } else if (d.charge_type == 'Actual' && d.account_head) { - frappe.model.set_value(cdt, cdn, "description", d.account_head.split(' - ')[0]); } } From 5b02d328269e2654f6253c45ddafe215121d2435 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 5 Nov 2020 18:04:14 +0530 Subject: [PATCH 082/283] fix: trailling spaces removed --- erpnext/public/js/controllers/accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index b1ffcfb6ad..1bceec0547 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -155,7 +155,7 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) { if(d.charge_type!=="Actual"){ frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0); frappe.model.set_value(cdt, cdn, "description", r.message.account_name); - } + } else if(d.charge_type == 'Actual'){ frappe.model.set_value(cdt, cdn, "description", r.message.account_name); } From 4d99d695a38d40087e2f9051084f1c9b9016f187 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Fri, 6 Nov 2020 23:33:19 +0530 Subject: [PATCH 083/283] feat(uae vat 201): update desk page entry --- erpnext/accounts/desk_page/accounting/accounting.json | 8 ++++---- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 64e2b246a7..de9ed9fca1 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -83,7 +83,7 @@ { "hidden": 0, "label": "Value-Added Tax (VAT UAE)", - "links": "[\n {\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -103,8 +103,8 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-10-08 20:31:46.022470", - "modified_by": "Administrator", + "modified": "2020-11-06 23:30:39.515679", + "modified_by": "moha@gmail.com", "module": "Accounts", "name": "Accounting", "onboarding": "Accounts", @@ -158,4 +158,4 @@ "type": "Dashboard" } ] -} +} \ No newline at end of file diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index de727d2ecd..b0614238ba 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -30,14 +30,12 @@ def get_columns(): "label": _("Amount (AED)"), "fieldtype": "Currency", "width": 125, - "options": "currency" }, { "fieldname": "vat_amount", "label": _("VAT Amount (AED)"), "fieldtype": "Currency", "width": 150, - "options": "currency" } ] From c5aad7b6e5f8419c4a2201fff60e20f05362897a Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Fri, 6 Nov 2020 23:36:50 +0530 Subject: [PATCH 084/283] feat(UAE VAT 201): reorder desk page listing --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index de9ed9fca1..993d2a625b 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -83,7 +83,7 @@ { "hidden": 0, "label": "Value-Added Tax (VAT UAE)", - "links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n\n]" } ], "category": "Modules", @@ -103,7 +103,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-11-06 23:30:39.515679", + "modified": "2020-11-06 23:34:50.325014", "modified_by": "moha@gmail.com", "module": "Accounts", "name": "Accounting", From 4f2a64479dfcaf6a4bc164315abb4865723f75fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Nov 2020 17:00:09 +0530 Subject: [PATCH 085/283] fix: Patch for old loans --- erpnext/patches/v13_0/update_old_loans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 77239429c5..eaeda093f5 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -70,7 +70,7 @@ def execute(): payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date FROM `tabJournal Entry` j, `tabJournal Entry Account` a WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s - and account = %s + and a.account = %s and j.docstatus = 1 ''', (loan.name, loan.loan_account), as_dict=1) for payment in payments: From 928dc432aba2cde284072b958c2ba3d801e4aeee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Nov 2020 17:17:12 +0530 Subject: [PATCH 086/283] fix: Reload journal entry account doc --- erpnext/patches/v13_0/update_old_loans.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index eaeda093f5..dd15f10e09 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -18,6 +18,7 @@ def execute(): frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail') frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual') frappe.reload_doc('accounts', 'doctype', 'gl_entry') + frappe.reload_doc('accounts', 'doctype', 'journal_entry_account') updated_loan_types = [] From b648b9cf93dcbc47a5f33fa49acbc601e14ff614 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 10 Nov 2020 18:50:02 +0530 Subject: [PATCH 087/283] fix: code review changes --- erpnext/public/js/controllers/accounts.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 1bceec0547..9c746ab50b 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -154,11 +154,8 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) { callback: function(r) { if(d.charge_type!=="Actual"){ frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0); - frappe.model.set_value(cdt, cdn, "description", r.message.account_name); - } - else if(d.charge_type == 'Actual'){ - frappe.model.set_value(cdt, cdn, "description", r.message.account_name); } + frappe.model.set_value(cdt, cdn, "description", r.message.account_name); } }) } From 1c969d64a2f928f78470c4dd42899d2393c6291f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Nov 2020 20:25:35 +0530 Subject: [PATCH 088/283] fix: Handle cases where same loan type is used for multiple companies --- erpnext/patches/v13_0/update_old_loans.py | 35 +++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index dd15f10e09..2925f0a5bc 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,7 +23,8 @@ def execute(): updated_loan_types = [] loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', - 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account']) + 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], + filters={'docstatus': 1}) for loan in loans: # Update details in Loan Types and Loan @@ -39,7 +40,26 @@ def execute(): penalty_account = create_account(company=loan.company, account_type='Income Account', account_name='Penalty Account', parent_account=group_income_account) - if not loan_type_company: + # Same loan type used for multiple companies + if loan_type_company and loan_type_company != loan.company: + # get loan type for appropriate company + loan_type_name = frappe.get_value('Loan Type', {'company': loan.company, + 'mode_of_payment': loan.mode_of_payment, 'loan_account': loan.loan_account, + 'payment_account': loan.payment_account, 'interest_income_account': loan.interest_income_account, + 'penalty_income_account': loan.penalty_income_account}, 'name') + + if not loan_type_name: + loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() + create_loan_type(loan, loan_type_name, penalty_account) + + # update loan type in loan + frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, + loan.name)) + + if loan_type_name not in updated_loan_types: + updated_loan_types.append(loan_type_name) + + elif not loan_type_company: loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type) loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company @@ -87,3 +107,14 @@ def execute(): jv.flags.ignore_links = True jv.cancel() +def create_loan_type(loan, loan_type_name, penalty_account): + loan_type_doc = frappe.new_doc('Loan Type') + loan_type_doc.loan_name = loan_type_name + loan_type_doc.is_term_loan = 1 + loan_type_doc.company = loan.company + loan_type_doc.mode_of_payment = loan.mode_of_payment + loan_type_doc.payment_account = loan.payment_account + loan_type_doc.loan_account = loan.loan_account + loan_type_doc.interest_income_account = loan.interest_income_account + loan_type_doc.penalty_income_account = penalty_account + loan_type_doc.submit() From 73bde45bc5f488906f911cef57e6035712b38cb0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Nov 2020 22:08:02 +0530 Subject: [PATCH 089/283] fix: Pass updated loan type --- erpnext/patches/v13_0/update_old_loans.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 2925f0a5bc..c16c2c81b8 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -29,6 +29,7 @@ def execute(): for loan in loans: # Update details in Loan Types and Loan loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company') + loan_type = loan.loan_type group_income_account = frappe.get_value('Account', {'company': loan.company, 'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')}) @@ -56,6 +57,7 @@ def execute(): frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, loan.name)) + loan_type = loan_type_name if loan_type_name not in updated_loan_types: updated_loan_types.append(loan_type_name) @@ -70,8 +72,9 @@ def execute(): loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() updated_loan_types.append(loan.loan_type) + loan_type = loan.loan_type - if loan.loan_type in updated_loan_types: + if loan_type in updated_loan_types: if loan.status == 'Fully Disbursed': status = 'Disbursed' elif loan.status == 'Repaid/Closed': @@ -85,7 +88,7 @@ def execute(): 'status': status }) - process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan.loan_type, + process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date @@ -96,7 +99,7 @@ def execute(): for payment in payments: repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan.loan_type, loan.company) + loan_type, loan.company) repayment_entry.amount_paid = payment.debit_in_account_currency repayment_entry.posting_date = payment.posting_date From 7e4d115e36d9026f8fe5ad60c6d80e56c19c826b Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 11 Nov 2020 09:52:27 +0530 Subject: [PATCH 090/283] fix: sider issues fixed --- erpnext/public/js/controllers/accounts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 9c746ab50b..29f35958e1 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -146,13 +146,13 @@ cur_frm.cscript.account_head = function(doc, cdt, cdn) { if(!d.charge_type && d.account_head){ frappe.msgprint(__("Please select Charge Type first")); frappe.model.set_value(cdt, cdn, "account_head", ""); - } else if(d.account_head) { + } else if (d.account_head) { frappe.call({ type:"GET", method: "erpnext.controllers.accounts_controller.get_tax_rate", args: {"account_head":d.account_head}, callback: function(r) { - if(d.charge_type!=="Actual"){ + if (d.charge_type!=="Actual") { frappe.model.set_value(cdt, cdn, "rate", r.message.tax_rate || 0); } frappe.model.set_value(cdt, cdn, "description", r.message.account_name); From 13d1dda74b88c8c46d0e3adf618433e687c6cda2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 11:07:17 +0530 Subject: [PATCH 091/283] fix: Handle loan type naming collisions --- erpnext/patches/v13_0/update_old_loans.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c16c2c81b8..70c1b7eb39 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -51,7 +51,7 @@ def execute(): if not loan_type_name: loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() - create_loan_type(loan, loan_type_name, penalty_account) + loan_type_name = create_loan_type(loan, loan_type_name, penalty_account) # update loan type in loan frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, @@ -111,6 +111,10 @@ def execute(): jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): + + if frappe.db.get_value('Loan Type', loan_type_name): + loan_type_name = loan_type_name + '-1' + loan_type_doc = frappe.new_doc('Loan Type') loan_type_doc.loan_name = loan_type_name loan_type_doc.is_term_loan = 1 From 0dc052e635d5b9846265807af29f704105c9afc4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 12:57:16 +0530 Subject: [PATCH 092/283] fix: Return loan type name --- erpnext/patches/v13_0/update_old_loans.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 70c1b7eb39..fcadc6273e 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -125,3 +125,5 @@ def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc.interest_income_account = loan.interest_income_account loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() + + return loan_type_name From a2dc1740df6d4dea70d76d19fabadbd2dc885c2e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 13:57:10 +0530 Subject: [PATCH 093/283] fix: Use autoname for loan creation --- erpnext/patches/v13_0/update_old_loans.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index fcadc6273e..23e4803029 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -5,6 +5,7 @@ from frappe.utils import nowdate from erpnext.accounts.doctype.account.test_account import create_account from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans from erpnext.loan_management.doctype.loan.loan import make_repayment_entry +from frappe.model.naming import make_autoname def execute(): @@ -50,7 +51,6 @@ def execute(): 'penalty_income_account': loan.penalty_income_account}, 'name') if not loan_type_name: - loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() loan_type_name = create_loan_type(loan, loan_type_name, penalty_account) # update loan type in loan @@ -111,12 +111,8 @@ def execute(): jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): - - if frappe.db.get_value('Loan Type', loan_type_name): - loan_type_name = loan_type_name + '-1' - loan_type_doc = frappe.new_doc('Loan Type') - loan_type_doc.loan_name = loan_type_name + loan_type_doc.loan_name = make_autoname("Loan Type-.####") loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company loan_type_doc.mode_of_payment = loan.mode_of_payment @@ -126,4 +122,4 @@ def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() - return loan_type_name + return loan_type_doc.name From b58dca8d942c18bc3ab0e1afa7ca7e744967c5c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 13:37:11 +0530 Subject: [PATCH 094/283] fix: Only update open loans --- erpnext/patches/v13_0/update_old_loans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 23e4803029..8ed789cf45 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -25,7 +25,7 @@ def execute(): loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], - filters={'docstatus': 1}) + filters={'docstatus': 1, 'status': ('!=', 'Closed')}) for loan in loans: # Update details in Loan Types and Loan From c51b340ddf46486cc98d92af03a9332c3e899517 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 18:43:43 +0530 Subject: [PATCH 095/283] fix: Update closed loans --- erpnext/patches/v13_0/update_old_loans.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 8ed789cf45..3042db331a 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,6 +23,14 @@ def execute(): updated_loan_types = [] + # Update old loan status as closed + loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` + where paid = 0 and docstatus = 1""", as_dict=1) + + loans_to_close = [d.parent for d in loans_list] + + frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) + loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], filters={'docstatus': 1, 'status': ('!=', 'Closed')}) @@ -91,7 +99,7 @@ def execute(): process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) - payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date + payments = frappe.db.sql(''' SELECT j.name, a.credit, a.credit_in_account_currency, j.posting_date FROM `tabJournal Entry` j, `tabJournal Entry Account` a WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s and a.account = %s and j.docstatus = 1 From 78690af440ca67b3dd9de60b585522f172dfc423 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 18:47:34 +0530 Subject: [PATCH 096/283] fix: Update only if loans to close --- erpnext/patches/v13_0/update_old_loans.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 3042db331a..c7f372e26f 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -29,7 +29,8 @@ def execute(): loans_to_close = [d.parent for d in loans_list] - frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) + if loans_to_close: + frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], From a862eb25e6c51b31eafe96f0bbfdb5f20d9d3cf2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 13 Nov 2020 17:57:57 +0530 Subject: [PATCH 097/283] fix: Make repayment entry only if amount exists --- erpnext/patches/v13_0/update_old_loans.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c7f372e26f..c4d9bdb7af 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -107,17 +107,18 @@ def execute(): ''', (loan.name, loan.loan_account), as_dict=1) for payment in payments: - repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan_type, loan.company) + if payment.credit_in_account_currency: + repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, + loan_type, loan.company) - repayment_entry.amount_paid = payment.debit_in_account_currency - repayment_entry.posting_date = payment.posting_date - repayment_entry.save() - repayment_entry.submit() + repayment_entry.amount_paid = payment.credit_in_account_currency + repayment_entry.posting_date = payment.posting_date + repayment_entry.save() + repayment_entry.submit() - jv = frappe.get_doc('Journal Entry', payment.name) - jv.flags.ignore_links = True - jv.cancel() + jv = frappe.get_doc('Journal Entry', payment.name) + jv.flags.ignore_links = True + jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc('Loan Type') From b8d0b546a7851fd24c9815a9e797a7717adab2b0 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 16 Nov 2020 12:58:38 +0530 Subject: [PATCH 098/283] feat(UAE VAT Format): use company address for emir --- erpnext/regional/united_arab_emirates/setup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 62156e46be..6f520f6ad6 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -56,7 +56,8 @@ def make_custom_fields(): fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select', - options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain'), + options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain', + fetch_from='company_address.emirate'), dict(fieldname='tourist_tax_return', label='Tax Refund provided to Tourists (AED)', insert_after='vat_emirate', fieldtype='Currency', print_hide=1, default='0'), ] @@ -100,6 +101,10 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Data', insert_after='supplier_name'), ], + 'Address': [ + dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', + options='Abu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') + ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, 'Purchase Receipt': purchase_invoice_fields + invoice_fields, From 2e1787300d1b881d638e9786c52d6f43f85f780a Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 16 Nov 2020 17:36:27 +0530 Subject: [PATCH 099/283] feat(UAE VAT 201): emirate according to branch --- .../doctype/sales_invoice/sales_invoice.js | 23 +++++++++++++++++++ .../regional/united_arab_emirates/setup.py | 13 ++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 502e65ed8d..803312072c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -446,6 +446,29 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.refresh_field("outstanding_amount"); this.frm.refresh_field("paid_amount"); this.frm.refresh_field("base_paid_amount"); + }, + + branch: function(){ + const me = this + if (this.frm.doc.branch) { + frappe.call({ + async: false, + method: "frappe.client.get_value", + args: { + "doctype": "Branch", + "filters": { + 'name': this.frm.doc.branch + }, + "fieldname": ['emirate'] + }, + callback: function (res) { + if (res.message && res.message['emirate']) { + me.frm.set_value("vat_emirate", res.message['emirate']) + me.frm.refresh_field("vat_emirate") + } + } + }) + } } }); diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 6f520f6ad6..2e546deb82 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -38,8 +38,9 @@ def make_custom_fields(): dict(fieldname='supplier_name_in_arabic', label='Supplier Name in Arabic', fieldtype='Read Only', insert_after='supplier_name', fetch_from='supplier.supplier_name_in_arabic', print_hide=1), - dict(fieldname='recoverable_standard_rated_expenses', label='Recoverable Standard Rated Expenses (AED)', - insert_after='permit_no', fieldtype='Currency', print_hide=1, default='0'), + dict(fieldname='recoverable_standard_rated_expenses', print_hide=1, default='0', + label='Recoverable Standard Rated Expenses (AED)', insert_after='permit_no', + fieldtype='Currency', ), dict(fieldname='reverse_charge', label='Reverse Charge Applicable', fieldtype='Select', insert_after='recoverable_standard_rated_expenses', print_hide=1, options='Y\nN', default='N'), @@ -55,6 +56,8 @@ def make_custom_fields(): dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), + dict(fieldname='branch', label='Branch', options='Branch', + fieldtype='Link', insert_after='company', print_hide=1), dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select', options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain', fetch_from='company_address.emirate'), @@ -103,7 +106,11 @@ def make_custom_fields(): ], 'Address': [ dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', - options='Abu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') + options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') + ], + 'Branch': [ + dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', + options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, From d2477e6cc5cd0acc9353e9dc8a0b23a729da6ff3 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Mon, 16 Nov 2020 18:47:05 +0530 Subject: [PATCH 100/283] fix(UAE VAT 201): remove branch parts --- .../doctype/sales_invoice/sales_invoice.js | 23 ------------------- .../regional/united_arab_emirates/setup.py | 6 ----- 2 files changed, 29 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 803312072c..502e65ed8d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -446,29 +446,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.refresh_field("outstanding_amount"); this.frm.refresh_field("paid_amount"); this.frm.refresh_field("base_paid_amount"); - }, - - branch: function(){ - const me = this - if (this.frm.doc.branch) { - frappe.call({ - async: false, - method: "frappe.client.get_value", - args: { - "doctype": "Branch", - "filters": { - 'name': this.frm.doc.branch - }, - "fieldname": ['emirate'] - }, - callback: function (res) { - if (res.message && res.message['emirate']) { - me.frm.set_value("vat_emirate", res.message['emirate']) - me.frm.refresh_field("vat_emirate") - } - } - }) - } } }); diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index 2e546deb82..013ae5cf73 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -56,8 +56,6 @@ def make_custom_fields(): dict(fieldname='customer_name_in_arabic', label='Customer Name in Arabic', fieldtype='Read Only', insert_after='customer_name', fetch_from='customer.customer_name_in_arabic', print_hide=1), - dict(fieldname='branch', label='Branch', options='Branch', - fieldtype='Link', insert_after='company', print_hide=1), dict(fieldname='vat_emirate', label='VAT Emirate', insert_after='permit_no', fieldtype='Select', options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain', fetch_from='company_address.emirate'), @@ -108,10 +106,6 @@ def make_custom_fields(): dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') ], - 'Branch': [ - dict(fieldname='emirate', label='Emirate', fieldtype='Select', insert_after='state', - options='\nAbu Dhabi\nAjman\nDubai\nFujairah\nRas Al Khaimah\nSharjah\nUmm Al Quwain') - ], 'Purchase Invoice': purchase_invoice_fields + invoice_fields, 'Purchase Order': purchase_invoice_fields + invoice_fields, 'Purchase Receipt': purchase_invoice_fields + invoice_fields, From 642819b955205e9faa64ae1e72c697c2a7950305 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 17 Nov 2020 09:47:10 +0530 Subject: [PATCH 101/283] fix: place of supply change when address changes --- erpnext/public/js/utils.js | 15 +++++++++++++++ erpnext/regional/india/utils.py | 1 + erpnext/selling/sales_common.js | 1 + 3 files changed, 17 insertions(+) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index ea2093eee1..b4fe412fe9 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -304,6 +304,21 @@ $.extend(erpnext.utils, { } frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); + }, + + set_place_of_supply: function(frm){ + frappe.call({ + method: "erpnext.regional.india.utils.get_place_of_supply", + args: { + "party_details": frm.doc, + "doctype": frm.doc.doctype + }, + callback: function(r){ + if(r.message){ + frm.set_value("place_of_supply", r.message) + } + } + }) } }); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index dd87f0f660..c774cb03be 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -135,6 +135,7 @@ def test_method(): '''test function''' return 'overridden' +@frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 002cfe41e1..77bdf2912f 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -117,6 +117,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ customer_address: function() { erpnext.utils.get_address_display(this.frm, "customer_address"); erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); + erpnext.utils.set_place_of_supply(this.frm) }, shipping_address_name: function() { From a77f2ba0def67756a4f3b91fe02847615df39e20 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 17 Nov 2020 15:34:05 +0530 Subject: [PATCH 102/283] fix: validation for membership --- 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 4c85cb60e8..7d15abaa3b 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -70,7 +70,7 @@ class Membership(Document): settings = frappe.get_doc("Membership Settings") if not member.customer: - frappe.throw(_("No customer linked to member {}", [member.name])) + frappe.throw(_("No customer linked to member {0}").format(frappe.bold(self.member))) if not settings.debit_account: frappe.throw(_("You need to set Debit Account in Membership Settings")) From 327143731465510840c3b8c6fe3e12187807ca03 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 17 Nov 2020 17:52:59 +0530 Subject: [PATCH 103/283] fix: Handle for custom field IFSC code in Bank remittance report. (#23905) * fix: Handel for custom field IFSC code * fix: changes_requested --- .../report/bank_remittance/bank_remittance.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py index 4b052bf5c4..500543ceb0 100644 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.py +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py @@ -47,33 +47,39 @@ def execute(filters=None): "fieldtype": "Int", "fieldname": "employee_account_no", "width": 50 - }, - { + } + ] + + if frappe.db.has_column('Employee', 'ifsc_code'): + columns.append({ "label": _("IFSC Code"), "fieldtype": "Data", "fieldname": "bank_code", "width": 100 - }, - { - "label": _("Currency"), - "fieldtype": "Data", - "fieldname": "currency", - "width": 50 - }, - { - "label": _("Net Salary Amount"), - "fieldtype": "Currency", - "options": "currency", - "fieldname": "amount", - "width": 100 - } - ] + }) + + columns += [{ + "label": _("Currency"), + "fieldtype": "Data", + "fieldname": "currency", + "width": 50 + }, + { + "label": _("Net Salary Amount"), + "fieldtype": "Currency", + "options": "currency", + "fieldname": "amount", + "width": 100 + }] + data = [] accounts = get_bank_accounts() payroll_entries = get_payroll_entries(accounts, filters) salary_slips = get_salary_slips(payroll_entries) - get_emp_bank_ifsc_code(salary_slips) + + if frappe.db.has_column('Employee', 'ifsc_code'): + get_emp_bank_ifsc_code(salary_slips) for salary in salary_slips: if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]: From 6b10d87d468acdf4e810d5a4b4da4b389def6a06 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 17 Nov 2020 20:34:51 +0530 Subject: [PATCH 104/283] fix: place of supply change on address change --- erpnext/public/js/controllers/buying.js | 1 + erpnext/public/js/utils.js | 28 ++++++++++++------------- erpnext/regional/india/utils.py | 3 +++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cb76c87b62..cd5cc9282b 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -135,6 +135,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ supplier_address: function() { erpnext.utils.get_address_display(this.frm); erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address"); + erpnext.utils.set_place_of_supply(this.frm) }, buying_price_list: function() { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index b4fe412fe9..1555896eac 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -116,6 +116,19 @@ $.extend(erpnext.utils, { } }, + set_place_of_supply: function(frm){ + frappe.call({ + method: "erpnext.regional.india.utils.get_place_of_supply", + args: { + "party_details": frm.doc, + "doctype": frm.doc.doctype + }, + callback: function(r){ + frm.set_value("place_of_supply", r.message) + } + }) + }, + add_indicator_for_multicompany: function(frm, info) { frm.dashboard.stats_area.removeClass('hidden'); frm.dashboard.stats_area_row.addClass('flex'); @@ -304,21 +317,6 @@ $.extend(erpnext.utils, { } frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); - }, - - set_place_of_supply: function(frm){ - frappe.call({ - method: "erpnext.regional.india.utils.get_place_of_supply", - args: { - "party_details": frm.doc, - "doctype": frm.doc.doctype - }, - callback: function(r){ - if(r.message){ - frm.set_value("place_of_supply", r.message) - } - } - }) } }); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index c774cb03be..7ad1c07f93 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -138,6 +138,9 @@ def test_method(): @frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return + if isinstance(party_details, string_types): + party_details = json.loads(party_details) + party_details = frappe._dict(party_details) if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): address_name = party_details.customer_address or party_details.shipping_address_name From 69232f8dd1a3061fa650f8cbac185d5933f0bf6b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Nov 2020 22:00:41 +0530 Subject: [PATCH 105/283] fix: Loan application link on creating loan --- .../loan_management/doctype/loan_application/loan_application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py index bac6e638d7..e59db4c12d 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application.py @@ -127,6 +127,7 @@ def create_loan(source_name, target_doc=None, submit=0): target_doc.loan_account = account_details.loan_account target_doc.interest_income_account = account_details.interest_income_account target_doc.penalty_income_account = account_details.penalty_income_account + target_doc.loan_application = source_name doclist = get_mapped_doc("Loan Application", source_name, { From d82b76fe0ace6076f1087c9c0ba7552b7611cd1b Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 18 Nov 2020 11:56:49 +0530 Subject: [PATCH 106/283] fix: pos item search includes non stock items (#23914) * fix: pos item search includes non stock items * chore: add validation for non stock items --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 9 +++++++++ erpnext/selling/page/point_of_sale/point_of_sale.py | 1 + 2 files changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index a7e20a0c32..d486ff6028 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -39,6 +39,7 @@ class POSInvoice(SalesInvoice): self.validate_serialised_or_batched_item() self.validate_stock_availablility() self.validate_return_items_qty() + self.validate_non_stock_items() self.set_status() self.set_account_for_mode_of_payment() self.validate_pos() @@ -174,6 +175,14 @@ class POSInvoice(SalesInvoice): _("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}") .format(d.idx, bold_serial_no, bold_return_against) ) + + def validate_non_stock_items(self): + for d in self.get("items"): + is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item") + if not is_stock_item: + frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice. ").format( + d.idx, frappe.bold(d.item_code) + ), title=_("Invalid Item")) def validate_mode_of_payment(self): if len(self.payments) == 0: diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index a690050f79..062cba19e6 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -62,6 +62,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va `tabItem` item {bin_join_selection} WHERE item.disabled = 0 + AND item.is_stock_item = 1 AND item.has_variants = 0 AND item.is_sales_item = 1 AND item.is_fixed_asset = 0 From 8c9b60edfec53a8a58ace5e5a3284efbdae7b724 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 18 Nov 2020 12:51:13 +0530 Subject: [PATCH 107/283] fix: reversing previous commits and adding condition in regional controller --- erpnext/public/js/controllers/buying.js | 1 - erpnext/public/js/utils.js | 13 ------------- erpnext/regional/india/taxes.js | 1 + erpnext/regional/india/utils.py | 23 +++++++++-------------- erpnext/selling/sales_common.js | 1 - 5 files changed, 10 insertions(+), 29 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cd5cc9282b..cb76c87b62 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -135,7 +135,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ supplier_address: function() { erpnext.utils.get_address_display(this.frm); erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address"); - erpnext.utils.set_place_of_supply(this.frm) }, buying_price_list: function() { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 1555896eac..ea2093eee1 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -116,19 +116,6 @@ $.extend(erpnext.utils, { } }, - set_place_of_supply: function(frm){ - frappe.call({ - method: "erpnext.regional.india.utils.get_place_of_supply", - args: { - "party_details": frm.doc, - "doctype": frm.doc.doctype - }, - callback: function(r){ - frm.set_value("place_of_supply", r.message) - } - }) - }, - add_indicator_for_multicompany: function(frm, info) { frm.dashboard.stats_area.removeClass('hidden'); frm.dashboard.stats_area_row.addClass('flex'); diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index 3b6a28f52c..ecfa9b7cdf 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -37,6 +37,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => { callback: function(r) { if(r.message) { frm.set_value('taxes_and_charges', r.message.taxes_and_charges); + frm.set_value('place_of_supply', r.message.place_of_supply); } else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) { frm.set_value('taxes_and_charges', ''); frm.set_value('taxes', []); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 7ad1c07f93..54083dea84 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -135,12 +135,8 @@ def test_method(): '''test function''' return 'overridden' -@frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return - if isinstance(party_details, string_types): - party_details = json.loads(party_details) - party_details = frappe._dict(party_details) if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): address_name = party_details.customer_address or party_details.shipping_address_name @@ -164,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return + return party_details if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -172,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.company_gstin: - return + return party_details elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.supplier_gstin: - return + return party_details - if not party_details.place_of_supply: return + if not party_details.place_of_supply: return party_details - if not party_details.company_gstin: return + if not party_details.company_gstin: return party_details if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -201,12 +197,11 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return + return party_details party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - if return_taxes: - return party_details + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 77bdf2912f..002cfe41e1 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -117,7 +117,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ customer_address: function() { erpnext.utils.get_address_display(this.frm, "customer_address"); erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); - erpnext.utils.set_place_of_supply(this.frm) }, shipping_address_name: function() { From 542bc017184db1187be0dcab948dbee0ba753a91 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 18 Nov 2020 15:00:34 +0530 Subject: [PATCH 108/283] fix(minor): update routes --- .../opening_invoice_creation_tool.py | 6 +++--- erpnext/assets/doctype/asset/asset.py | 8 ++++---- .../supplier_quotation/supplier_quotation.py | 2 +- erpnext/config/education.py | 2 +- erpnext/config/projects.py | 6 +++--- .../assessment_result_tool.js | 2 +- .../course_scheduling_tool.js | 2 +- .../program_enrollment/program_enrollment.py | 2 +- .../tally_migration/tally_migration.js | 4 ++-- .../clinical_procedure/clinical_procedure.js | 2 +- .../inpatient_record/inpatient_record.py | 2 +- .../patient_appointment.py | 4 ++-- erpnext/healthcare/utils.py | 10 +++++----- .../employee_transfer/employee_transfer.py | 2 +- .../leave_allocation/leave_allocation.py | 2 +- .../leave_application/leave_application.py | 2 +- .../hr/doctype/shift_request/shift_request.py | 2 +- erpnext/hr/utils.py | 2 +- erpnext/manufacturing/doctype/bom/bom.js | 2 +- .../doctype/bom/bom_item_preview.html | 4 ++-- .../production_plan/production_plan.py | 4 ++-- .../bom_stock_report/bom_stock_report.js | 4 ++-- .../doctype/payroll_period/payroll_period.py | 2 +- erpnext/projects/doctype/task/task_list.js | 2 +- erpnext/public/js/call_popup/call_popup.js | 4 ++-- erpnext/public/js/communication.js | 2 +- .../js/education/assessment_result_tool.html | 6 +++--- .../doctype/sales_order/sales_order.js | 4 ++-- .../page/point_of_sale/pos_controller.js | 20 +++++++++---------- .../welcome_to_erpnext.html | 1 - erpnext/stock/dashboard/item_dashboard.js | 2 +- erpnext/stock/doctype/batch/batch.js | 2 +- erpnext/stock/doctype/item/item.js | 4 ++-- erpnext/stock/doctype/item/item.py | 2 +- .../stock/doctype/item_price/item_price.js | 2 +- erpnext/support/doctype/issue/issue.js | 2 +- erpnext/support/doctype/issue/issue.py | 2 +- erpnext/utilities/bot.py | 4 ++-- 38 files changed, 68 insertions(+), 69 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index d51856a8a4..0e1d140b2e 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -64,11 +64,11 @@ class OpeningInvoiceCreationTool(Document): prepare_invoice_summary(doctype, invoices) return invoices_summary, max_count - + def validate_company(self): if not self.company: frappe.throw(_("Please select the Company")) - + def set_missing_values(self, row): row.qty = row.qty or 1.0 row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company) @@ -209,7 +209,7 @@ def start_import(invoices): frappe.db.commit() if errors: frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details") - .format(errors, "Error Log"), indicator="red", title=_("Error Occured")) + .format(errors, "Error Log"), indicator="red", title=_("Error Occured")) return names def publish(index, total, doctype): diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 30abc66a02..1793dad494 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -471,7 +471,7 @@ class Asset(AccountsController): asset_bought_with_invoice = (purchase_document == self.purchase_invoice) fixed_asset_account = self.get_fixed_asset_account() - + cwip_enabled = is_cwip_accounting_enabled(self.asset_category) cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled) @@ -503,10 +503,10 @@ class Asset(AccountsController): purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt return purchase_document - + def get_fixed_asset_account(self): return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company) - + def get_cwip_account(self, cwip_enabled=False): cwip_account = None try: @@ -659,7 +659,7 @@ def transfer_asset(args): frappe.db.commit() - frappe.msgprint(_("Asset Movement record {0} created").format("{0}").format(movement_entry.name)) + frappe.msgprint(_("Asset Movement record {0} created").format("{0}").format(movement_entry.name)) @frappe.whitelist() def get_item_details(item_code, asset_category): diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index ae5611f3c4..6a4c02c075 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -71,7 +71,7 @@ class SupplierQuotation(BuyingController): doc_sup = doc_sup[0] if doc_sup else None if not doc_sup: frappe.throw(_("Supplier {0} not found in {1}").format(self.supplier, - " Request for Quotation {0} ".format(doc.name))) + " Request for Quotation {0} ".format(doc.name))) quote_status = _('Received') for item in doc.items: diff --git a/erpnext/config/education.py b/erpnext/config/education.py index 4efaaa65cd..1c8ab10f53 100644 --- a/erpnext/config/education.py +++ b/erpnext/config/education.py @@ -173,7 +173,7 @@ def get_data(): { "type": "doctype", "name": "Course Schedule", - "route": "#List/Course Schedule/Calendar" + "route": "/app/List/Course Schedule/Calendar" }, { "type": "doctype", diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py index 47700d10b2..ab4db96477 100644 --- a/erpnext/config/projects.py +++ b/erpnext/config/projects.py @@ -16,13 +16,13 @@ def get_data(): { "type": "doctype", "name": "Task", - "route": "#List/Task", + "route": "/app/List/Task", "description": _("Project activity / task."), "onboard": 1, }, { "type": "report", - "route": "#List/Task/Gantt", + "route": "/app/List/Task/Gantt", "doctype": "Task", "name": "Gantt Chart", "description": _("Gantt chart of all tasks."), @@ -97,5 +97,5 @@ def get_data(): }, ] }, - + ] diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js index 3cd451209f..e213309c5e 100644 --- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js +++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js @@ -128,7 +128,7 @@ frappe.ui.form.on('Assessment Result Tool', { result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade); let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`); $(link_span).css("display", "block"); - $(link_span).find("a").attr("href", "#Form/Assessment Result/"+assessment_result.name); + $(link_span).find("a").attr("href", "/desk/Form/Assessment Result/"+assessment_result.name); } }); } diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js index 20503f919c..f408dae7bd 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Course Scheduling Tool', {
${course_schedules.map( - c => ` + c => `` ).join('')} diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index 6fbcd8aa97..3045db7f32 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -87,7 +87,7 @@ class ProgramEnrollment(Document): fees.submit() fee_list.append(fees.name) if fee_list: - fee_list = ["""%s""" % \ + fee_list = ["""%s""" % \ (fee, fee) for fee in fee_list] msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list))) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index fd16d1e84a..e8641114be 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -23,10 +23,10 @@ frappe.ui.form.on("Tally Migration", { frappe.msgprint({ message: __("An error has occurred during {0}. Check {1} for more details", [ - repl("%(tally_document)s", { + repl("%(tally_document)s", { tally_document: frm.docname }), - "Error Log" + "Error Log" ] ), title: __("Tally Migration Error"), diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index eb7d4bdeba..19bddbb191 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -86,7 +86,7 @@ frappe.ui.form.on('Clinical Procedure', { if (r.message) { frappe.show_alert({ message: __('Stock Entry {0} created', - ['' + r.message + '']), + ['' + r.message + '']), indicator: 'green' }); } diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index bc76970601..c7ab447860 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -50,7 +50,7 @@ class InpatientRecord(Document): if ip_record: msg = _(("Already {0} Patient {1} with Inpatient Record ").format(ip_record[0].status, self.patient) \ - + """ {0}""".format(ip_record[0].name)) + + """ {0}""".format(ip_record[0].name)) frappe.throw(msg) def admit(self, service_unit, check_in, expected_discharge=None): diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index e685b20a8c..90d9023278 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -63,7 +63,7 @@ class PatientAppointment(Document): if overlaps: overlapping_details = _('Appointment overlaps with ') - overlapping_details += "{0}
".format(overlaps[0][0]) + overlapping_details += "{0}
".format(overlaps[0][0]) overlapping_details += _('{0} has appointment scheduled with {1} at {2} having {3} minute(s) duration.').format( overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4]) frappe.throw(overlapping_details, title=_('Appointments Overlapping')) @@ -75,7 +75,7 @@ class PatientAppointment(Document): if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'): if not frappe.db.get_value('Patient', self.patient, 'customer'): msg = _("Please set a Customer linked to the Patient") - msg += " {0}".format(self.patient) + msg += " {0}".format(self.patient) frappe.throw(msg, title=_('Customer Not Found')) def update_prescription_details(self): diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 96282f50a9..248692332c 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -32,7 +32,7 @@ def get_healthcare_services_to_invoice(patient, company): 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) + msg += " {0}".format(patient.name) frappe.throw(msg, title=_('Customer Not Found')) @@ -169,7 +169,7 @@ def get_clinical_procedures_to_invoice(patient, company): service_item = get_healthcare_service_item('clinical_procedure_consumable_item') if not service_item: msg = _('Please Configure Clinical Procedure Consumable Item in ') - msg += '''Healthcare Settings''' + msg += '''Healthcare Settings''' frappe.throw(msg, title=_('Missing Configuration')) clinical_procedures_to_invoice.append({ @@ -324,7 +324,7 @@ def throw_config_service_item(is_inpatient): service_item_label = _('Inpatient Visit Charge Item') msg = _(('Please Configure {0} in ').format(service_item_label) \ - + '''Healthcare Settings''') + + '''Healthcare Settings''') frappe.throw(msg, title=_('Missing Configuration')) @@ -334,7 +334,7 @@ def throw_config_practitioner_charge(is_inpatient, practitioner): charge_name = _('Inpatient Visit Charge') msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \ - + ''' {0}'''.format(practitioner)) + + ''' {0}'''.format(practitioner)) frappe.throw(msg, title=_('Missing Configuration')) @@ -654,6 +654,6 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): >
" \ + section_html + html +'
' if doc_html: - doc_html = "
" %(doctype, docname) + doc_html + '
' + doc_html = "
" %(doctype, docname) + doc_html + '
' return {'html': doc_html} diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index c730e022a5..37d616f14d 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -50,7 +50,7 @@ class EmployeeTransfer(Document): employee = frappe.get_doc("Employee", self.employee) if self.create_new_employee_id: if self.new_employee_id: - frappe.throw(_("Please delete the Employee {0}\ + frappe.throw(_("Please delete the Employee {0}\ to cancel this document").format(self.new_employee_id)) #mark the employee as active employee.status = "Active" diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 03fe3fa035..32c5456239 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -82,7 +82,7 @@ class LeaveAllocation(Document): frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}") .format(self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date))) - frappe.throw(_('Reference') + ': {0}' + frappe.throw(_('Reference') + ': {0}' .format(leave_allocation[0][0]), OverlapError) def validate_back_dated_allocation(self): diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 3f25f58383..35c3ea77ea 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -246,7 +246,7 @@ class LeaveApplication(Document): def throw_overlap_error(self, d): msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee, d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \ - + """ {0}""".format(d["name"]) + + """ {0}""".format(d["name"]) frappe.throw(msg, OverlapError) def get_total_leaves_on_half_day(self): diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py index 1c2801bf08..473193d5ac 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -87,5 +87,5 @@ class ShiftRequest(Document): def throw_overlap_error(self, d): msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee, d['shift_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \ - + """ {0}""".format(d["name"]) + + """ {0}""".format(d["name"]) frappe.throw(msg, OverlapError) \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 8d95924681..3b8d73b0f0 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -211,7 +211,7 @@ def get_doc_condition(doctype): def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date): msg = _("A {0} exists between {1} and {2} (").format(doc.doctype, formatdate(from_date), formatdate(to_date)) \ - + """ {1}""".format(doc.doctype, overlap_doc) \ + + """ {1}""".format(doc.doctype, overlap_doc) \ + _(") for {0}").format(exists_for) frappe.throw(msg) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 1c4b7a1e1c..55f7a1b8a9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -134,7 +134,7 @@ frappe.ui.form.on("BOM", { frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}', [ `variants`, - `${frm.doc.item}`, + `${frm.doc.item}`, ]), true); frm.$wrapper.find(".variants-intro").on("click", () => { diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html index c782f7bf0e..6cd5f8cb3c 100644 --- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html +++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html @@ -12,11 +12,11 @@

{% if data.value %} - + {{ __("Open BOM {0}", [data.value.bold()]) }} {% endif %} {% if data.item_code %} - + {{ __("Open Item {0}", [data.item_code.bold()]) }} {% endif %}

diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3833e86d27..8f9dd05217 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -319,7 +319,7 @@ class ProductionPlan(Document): frappe.flags.mute_messages = False if wo_list: - wo_list = ["""%s""" % \ + wo_list = ["""%s""" % \ (p, p) for p in wo_list] msgprint(_("{0} created").format(comma_and(wo_list))) else : @@ -423,7 +423,7 @@ class ProductionPlan(Document): frappe.flags.mute_messages = False if material_request_list: - material_request_list = ["""{1}""".format(m.name, m.name) \ + material_request_list = ["""{1}""".format(m.name, m.name) \ for m in material_request_list] msgprint(_("{0} created").format(comma_and(material_request_list))) else : diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 2ac6fa073b..2f18d1704b 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -27,9 +27,9 @@ frappe.query_reports["BOM Stock Report"] = { value = default_formatter(value, row, column, data); if (column.id == "Item"){ if (data["Enough Parts to Build"] > 0){ - value = `${data['Item']}` + value = `${data['Item']}` } else { - value = `${data['Item']}` + value = `${data['Item']}` } } return value diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py index d7893d0657..1c8cc53deb 100644 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.py +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py @@ -41,7 +41,7 @@ class PayrollPeriod(Document): if overlap_doc: msg = _("A {0} exists between {1} and {2} (").format(self.doctype, formatdate(self.start_date), formatdate(self.end_date)) \ - + """ {1}""".format(self.doctype, overlap_doc[0].name) \ + + """ {1}""".format(self.doctype, overlap_doc[0].name) \ + _(") for {0}").format(self.company) frappe.throw(msg) diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js index 941fe97546..7c620317de 100644 --- a/erpnext/projects/doctype/task/task_list.js +++ b/erpnext/projects/doctype/task/task_list.js @@ -26,7 +26,7 @@ frappe.listview_settings['Task'] = { }, gantt_custom_popup_html: function(ganttobj, task) { var html = `
${ganttobj.name}
`; + href="/desk/Form/Task/${ganttobj.id}""> ${ganttobj.name} `; if(task.project) html += `

Project: ${task.project}

`; html += `

Progress: ${ganttobj.progress}

`; diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 5e4d4a585f..378d6d131d 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -85,7 +85,7 @@ class CallPopup {
+ href="/desk/Form/Call Log/${this.call_log.name}"> ${__('View call log')} `, @@ -167,7 +167,7 @@ class CallPopup { const issue_field = this.dialog.get_field("last_issue"); issue_field.set_value(issue.subject); issue_field.$wrapper.append(` - + ${__('View all issues from {0}', [issue.customer])} `); diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index 26e5ab8b32..38778e2ab0 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -84,7 +84,7 @@ frappe.ui.form.on("Communication", { frm.reload_doc(); frappe.show_alert({ message: __("Opportunity {0} created", - ['' + r.message + '']), + ['' + r.message + '']), indicator: 'green' }); } diff --git a/erpnext/public/js/education/assessment_result_tool.html b/erpnext/public/js/education/assessment_result_tool.html index 9fc17f7be1..b591010ec8 100644 --- a/erpnext/public/js/education/assessment_result_tool.html +++ b/erpnext/public/js/education/assessment_result_tool.html @@ -19,7 +19,7 @@ {% for s in students %} - @@ -29,7 +29,7 @@ From a339752ba4419a5a00530286499282ebb0cb4216 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 25 Nov 2020 08:54:51 +0530 Subject: [PATCH 149/283] fix: Loan disbursement amount validation (#24000) --- erpnext/loan_management/doctype/loan/loan.json | 3 ++- .../doctype/loan_disbursement/loan_disbursement.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index e8ecf015c3..d468f52bc0 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -332,6 +332,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.is_secured_loan", "fetch_from": "loan_application.maximum_loan_amount", "fieldname": "maximum_loan_amount", "fieldtype": "Currency", @@ -352,7 +353,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-11-05 10:04:00.762975", + "modified": "2020-11-24 12:27:23.208240", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index 233862bcfe..f341e81065 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -171,10 +171,10 @@ def get_total_pledged_security_value(loan): return security_value @frappe.whitelist() -def get_disbursal_amount(loan): - loan_details = frappe.get_all("Loan", fields = ["loan_amount", "disbursed_amount", "total_payment", - "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan"], - filters= { "name": loan })[0] +def get_disbursal_amount(loan, on_current_security_price=0): + loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment", + "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan", + "maximum_loan_amount"], as_dict=1) if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan, 'status': 'Pending'}): @@ -188,9 +188,12 @@ def get_disbursal_amount(loan): - flt(loan_details.total_principal_paid) security_value = 0.0 - if loan_details.is_secured_loan: + if loan_details.is_secured_loan and on_current_security_price: security_value = get_total_pledged_security_value(loan) + if loan_details.is_secured_loan and not on_current_security_price: + security_value = flt(loan_details.maximum_loan_amount) + if not security_value and not loan_details.is_secured_loan: security_value = flt(loan_details.loan_amount) From c66bd45ba46b9e7c6ebd54eda42db4e6fb57761a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 25 Nov 2020 09:09:40 +0530 Subject: [PATCH 150/283] feat: Inpatient Medication Orders Script Report (#23984) * feat: Inpatient Medication Orders Script Report * feat: add chart for Inpatient Medication Order Report * feat: add report to Desk Page * feat: added filters for dates and healthcare service unit * test: Inpatient Medication Orders report --- .../desk_page/healthcare/healthcare.json | 4 +- .../inpatient_medication_entry.py | 4 +- .../inpatient_medication_orders/__init__.py | 0 .../inpatient_medication_orders.js | 57 +++++ .../inpatient_medication_orders.json | 36 ++++ .../inpatient_medication_orders.py | 198 ++++++++++++++++++ .../test_inpatient_medication_orders.py | 128 +++++++++++ 7 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 erpnext/healthcare/report/inpatient_medication_orders/__init__.py create mode 100644 erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js create mode 100644 erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json create mode 100644 erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py create mode 100644 erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 6546b08db9..81d60481ce 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -43,7 +43,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]" + "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Inpatient Medication Orders\",\n\t\t\"doctype\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Orders\"\n\t}\n]" } ], "category": "Domains", @@ -64,7 +64,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-06-25 23:50:56.951698", + "modified": "2020-11-23 23:00:48.764377", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py index 23e75196ee..5dac23abd9 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py @@ -274,4 +274,6 @@ def get_filters(entry): def get_current_healthcare_service_unit(inpatient_record): ip_record = frappe.get_doc('Inpatient Record', inpatient_record) - return ip_record.inpatient_occupancies[-1].service_unit \ No newline at end of file + if ip_record.inpatient_occupancies: + return ip_record.inpatient_occupancies[-1].service_unit + return \ No newline at end of file diff --git a/erpnext/healthcare/report/inpatient_medication_orders/__init__.py b/erpnext/healthcare/report/inpatient_medication_orders/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js new file mode 100644 index 0000000000..a10f83760f --- /dev/null +++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js @@ -0,0 +1,57 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Inpatient Medication Orders"] = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + 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.now_date(), + reqd: 1 + }, + { + fieldname: "patient", + label: __("Patient"), + fieldtype: "Link", + options: "Patient" + }, + { + fieldname: "service_unit", + label: __("Healthcare Service Unit"), + fieldtype: "Link", + options: "Healthcare Service Unit", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + 'company': company, + 'is_group': 0 + } + } + } + }, + { + fieldname: "show_completed_orders", + label: __("Show Completed Orders"), + fieldtype: "Check", + default: 1 + } + ] +}; diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json new file mode 100644 index 0000000000..9217fa1891 --- /dev/null +++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2020-11-23 17:25:58.802949", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "json": "{}", + "modified": "2020-11-23 19:40:20.227591", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Inpatient Medication Orders", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Inpatient Medication Order", + "report_name": "Inpatient Medication Orders", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "Healthcare Administrator" + }, + { + "role": "Nursing User" + }, + { + "role": "Physician" + } + ] +} \ No newline at end of file diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py new file mode 100644 index 0000000000..b9077301ba --- /dev/null +++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py @@ -0,0 +1,198 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit + +def execute(filters=None): + columns = get_columns() + data = get_data(filters) + chart = get_chart_data(data) + + return columns, data, None, chart + +def get_columns(): + return [ + { + "fieldname": "patient", + "fieldtype": "Link", + "label": "Patient", + "options": "Patient", + "width": 200 + }, + { + "fieldname": "healthcare_service_unit", + "fieldtype": "Link", + "label": "Healthcare Service Unit", + "options": "Healthcare Service Unit", + "width": 150 + }, + { + "fieldname": "drug", + "fieldtype": "Link", + "label": "Drug Code", + "options": "Item", + "width": 150 + }, + { + "fieldname": "drug_name", + "fieldtype": "Data", + "label": "Drug Name", + "width": 150 + }, + { + "fieldname": "dosage", + "fieldtype": "Link", + "label": "Dosage", + "options": "Prescription Dosage", + "width": 80 + }, + { + "fieldname": "dosage_form", + "fieldtype": "Link", + "label": "Dosage Form", + "options": "Dosage Form", + "width": 100 + }, + { + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "width": 100 + }, + { + "fieldname": "time", + "fieldtype": "Time", + "label": "Time", + "width": 100 + }, + { + "fieldname": "is_completed", + "fieldtype": "Check", + "label": "Is Order Completed", + "width": 100 + }, + { + "fieldname": "healthcare_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner", + "options": "Healthcare Practitioner", + "width": 200 + }, + { + "fieldname": "inpatient_medication_entry", + "fieldtype": "Link", + "label": "Inpatient Medication Entry", + "options": "Inpatient Medication Entry", + "width": 200 + }, + { + "fieldname": "inpatient_record", + "fieldtype": "Link", + "label": "Inpatient Record", + "options": "Inpatient Record", + "width": 200 + } + ] + +def get_data(filters): + conditions, values = get_conditions(filters) + + data = frappe.db.sql(""" + SELECT + parent.patient, parent.inpatient_record, parent.practitioner, + child.drug, child.drug_name, child.dosage, child.dosage_form, + child.date, child.time, child.is_completed, child.name + FROM `tabInpatient Medication Order` parent + INNER JOIN `tabInpatient Medication Order Entry` child + ON child.parent = parent.name + WHERE + parent.docstatus = 1 + {conditions} + ORDER BY date, time + """.format(conditions=conditions), values, as_dict=1) + + data = get_inpatient_details(data, filters.get("service_unit")) + + return data + +def get_conditions(filters): + conditions = "" + values = dict() + + if filters.get("company"): + conditions += " AND parent.company = %(company)s" + values["company"] = filters.get("company") + + if filters.get("from_date") and filters.get("to_date"): + conditions += " AND child.date BETWEEN %(from_date)s and %(to_date)s" + values["from_date"] = filters.get("from_date") + values["to_date"] = filters.get("to_date") + + if filters.get("patient"): + conditions += " AND parent.patient = %(patient)s" + values["patient"] = filters.get("patient") + + if not filters.get("show_completed_orders"): + conditions += " AND child.is_completed = 0" + + return conditions, values + + +def get_inpatient_details(data, service_unit): + service_unit_filtered_data = [] + + for entry in data: + entry["healthcare_service_unit"] = get_current_healthcare_service_unit(entry.inpatient_record) + if entry.is_completed: + entry["inpatient_medication_entry"] = get_inpatient_medication_entry(entry.name) + + if service_unit and entry.healthcare_service_unit and service_unit != entry.healthcare_service_unit: + service_unit_filtered_data.append(entry) + + entry.pop("name", None) + + for entry in service_unit_filtered_data: + data.remove(entry) + + return data + +def get_inpatient_medication_entry(order_entry): + return frappe.db.get_value("Inpatient Medication Entry Detail", {"against_imoe": order_entry}, "parent") + +def get_chart_data(data): + if not data: + return None + + labels = ["Pending", "Completed"] + datasets = [] + + status_wise_data = { + "Pending": 0, + "Completed": 0 + } + + for d in data: + if d.is_completed: + status_wise_data["Completed"] += 1 + else: + status_wise_data["Pending"] += 1 + + datasets.append({ + "name": "Inpatient Medication Order Status", + "values": [status_wise_data.get("Pending"), status_wise_data.get("Completed")] + }) + + chart = { + "data": { + "labels": labels, + "datasets": datasets + }, + "type": "donut", + "height": 300 + } + + chart["fieldtype"] = "Data" + + return chart \ No newline at end of file diff --git a/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py new file mode 100644 index 0000000000..0d3f45f500 --- /dev/null +++ b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py @@ -0,0 +1,128 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import unittest +import frappe +import datetime +from frappe.utils import getdate, now_datetime +from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy +from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge +from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme +from erpnext.healthcare.report.inpatient_medication_orders.inpatient_medication_orders import execute + +class TestInpatientMedicationOrders(unittest.TestCase): + @classmethod + def setUpClass(self): + frappe.db.sql("delete from `tabInpatient Medication Order` where company='_Test Company'") + frappe.db.sql("delete from `tabInpatient Medication Entry` where company='_Test Company'") + self.patient = create_patient() + self.ip_record = create_records(self.patient) + + def test_inpatient_medication_orders_report(self): + filters = { + 'company': '_Test Company', + 'from_date': getdate(), + 'to_date': getdate(), + 'patient': '_Test IPD Patient', + 'service_unit': 'Test Service Unit Ip Occupancy - _TC' + } + + report = execute(filters) + + expected_data = [ + { + 'patient': '_Test IPD Patient', + 'inpatient_record': self.ip_record.name, + 'practitioner': None, + 'drug': 'Dextromethorphan', + 'drug_name': 'Dextromethorphan', + 'dosage': 1.0, + 'dosage_form': 'Tablet', + 'date': getdate(), + 'time': datetime.timedelta(seconds=32400), + 'is_completed': 0, + 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC' + }, + { + 'patient': '_Test IPD Patient', + 'inpatient_record': self.ip_record.name, + 'practitioner': None, + 'drug': 'Dextromethorphan', + 'drug_name': 'Dextromethorphan', + 'dosage': 1.0, + 'dosage_form': 'Tablet', + 'date': getdate(), + 'time': datetime.timedelta(seconds=50400), + 'is_completed': 0, + 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC' + }, + { + 'patient': '_Test IPD Patient', + 'inpatient_record': self.ip_record.name, + 'practitioner': None, + 'drug': 'Dextromethorphan', + 'drug_name': 'Dextromethorphan', + 'dosage': 1.0, + 'dosage_form': 'Tablet', + 'date': getdate(), + 'time': datetime.timedelta(seconds=75600), + 'is_completed': 0, + 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC' + } + ] + + self.assertEqual(expected_data, report[1]) + + filters = frappe._dict(from_date=getdate(), to_date=getdate(), from_time='', to_time='') + ipme = create_ipme(filters) + ipme.submit() + + filters = { + 'company': '_Test Company', + 'from_date': getdate(), + 'to_date': getdate(), + 'patient': '_Test IPD Patient', + 'service_unit': 'Test Service Unit Ip Occupancy - _TC', + 'show_completed_orders': 0 + } + + report = execute(filters) + self.assertEqual(len(report[1]), 0) + + def tearDown(self): + if frappe.db.get_value('Patient', self.patient, 'inpatient_record'): + # cleanup - Discharge + schedule_discharge(frappe.as_json({'patient': self.patient})) + self.ip_record.reload() + mark_invoiced_inpatient_occupancy(self.ip_record) + + self.ip_record.reload() + discharge_patient(self.ip_record) + + for entry in frappe.get_all('Inpatient Medication Entry'): + doc = frappe.get_doc('Inpatient Medication Entry', entry.name) + doc.cancel() + doc.delete() + + for entry in frappe.get_all('Inpatient Medication Order'): + doc = frappe.get_doc('Inpatient Medication Order', entry.name) + doc.cancel() + doc.delete() + + +def create_records(patient): + frappe.db.sql("""delete from `tabInpatient Record`""") + + # Admit + ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 + ip_record.save() + ip_record.reload() + service_unit = get_healthcare_service_unit() + admit_patient(ip_record, service_unit, now_datetime()) + + ipmo = create_ipmo(patient) + ipmo.submit() + + return ip_record From fbcc3c1b7006069d9bb9739ef57dc3776675a3b4 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 25 Nov 2020 04:41:51 +0100 Subject: [PATCH 151/283] fix: Translatable strings (#23783) * fix: start_pattern * fix: translatable strings * fix: add missing semicolon (task) * fix: add missing semicolon (setup_wizard) * fix: text should start on the same line Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: move out HTML element as variable Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: pull out message, translate "Undo". Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: typo Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: text should start on the same line Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * Revert "fix: start_pattern" This reverts commit decc62e2ab75f45db1df022fe13780c2d0d2560d. Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../chart_of_accounts_importer.js | 3 +-- .../bank_reconciliation.js | 6 ++---- erpnext/assets/doctype/asset/asset.js | 6 +++--- .../appointment_booking_settings.js | 2 +- erpnext/projects/doctype/task/task.js | 5 ++++- erpnext/public/js/hub/pages/Category.vue | 2 +- erpnext/public/js/hub/pages/FeaturedItems.vue | 12 +++++------- erpnext/public/js/hub/pages/Item.vue | 8 ++++---- erpnext/public/js/hub/pages/NotFound.vue | 2 +- erpnext/public/js/hub/pages/Publish.vue | 19 +++++++------------ erpnext/public/js/hub/pages/SavedItems.vue | 11 ++++++++--- erpnext/public/js/hub/pages/Search.vue | 5 ++++- erpnext/public/js/hub/pages/Seller.vue | 4 ++-- erpnext/public/js/setup_wizard.js | 5 ++++- 14 files changed, 47 insertions(+), 43 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index 2235298201..f795dfa83e 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -94,8 +94,7 @@ frappe.ui.form.on('Chart of Accounts Importer', { callback: function(r) { if(r.message===false) { frm.set_value("company", ""); - frappe.throw(__(`Transactions against the company already exist! - Chart Of accounts can be imported for company with no transactions`)); + frappe.throw(__("Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions.")); } else { frm.trigger("refresh"); } diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js index 9703527875..6ae81d7402 100644 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js @@ -156,7 +156,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { setup_transactions_dom() { const me = this; - me.parent.$main_section.append(`
`) + me.parent.$main_section.append('
'); } create_datatable() { @@ -167,9 +167,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { }) } catch(err) { - let msg = __(`Your file could not be processed by ERPNext. -
It should be a standard CSV or XLSX file. -
The headers should be in the first row.`) + let msg = __("Your file could not be processed. It should be a standard CSV or XLSX file with headers in the first row."); frappe.throw(msg) } diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 7ad164a8b9..b2318a2bc6 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -373,8 +373,8 @@ frappe.ui.form.on('Asset', { doctype_field = frappe.scrub(doctype) frm.set_value(doctype_field, ''); frappe.msgprint({ - title: __(`Invalid ${doctype}`), - message: __(`The selected ${doctype} doesn't contains selected Asset Item.`), + title: __('Invalid {0}', [__(doctype)]), + message: __('The selected {0} does not contain the selected Asset Item.', [__(doctype)]), indicator: 'red' }); } @@ -436,7 +436,7 @@ frappe.ui.form.on('Asset Finance Book', { depreciation_start_date: function(frm, cdt, cdn) { const book = locals[cdt][cdn]; if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) { - frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`)); + frappe.msgprint(__("Depreciation Posting Date should not be equal to Available for Use Date.")); book.depreciation_start_date = ""; frm.refresh_field("finance_books"); } diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js index 99b82148d2..dc3ae8bf41 100644 --- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js @@ -4,7 +4,7 @@ function check_times(frm) { let from_time = Date.parse('01/01/2019 ' + d.from_time); let to_time = Date.parse('01/01/2019 ' + d.to_time); if (from_time > to_time) { - frappe.throw(__(`In row ${i + 1} of Appointment Booking Slots : "To Time" must be later than "From Time"`)); + frappe.throw(__('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [i + 1])); } }); } \ No newline at end of file diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index 8c6a9cf8d7..002ddb2f40 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -49,7 +49,10 @@ frappe.ui.form.on("Task", { }, callback: function (r) { if (r.message.length > 0) { - frappe.msgprint(__(`Cannot convert it to non-group. The following child Tasks exist: ${r.message.join(", ")}.`)); + let message = __('Cannot convert Task to non-group because the following child Tasks exist: {0}.', + [r.message.join(", ")] + ); + frappe.msgprint(message); frm.reload_doc(); } } diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue index 057fe8bc61..16d06018ff 100644 --- a/erpnext/public/js/hub/pages/Category.vue +++ b/erpnext/public/js/hub/pages/Category.vue @@ -32,7 +32,7 @@ export default { item_id_fieldname: 'name', // Constants - empty_state_message: __(`No items in this category yet.`), + empty_state_message: __('No items in this category yet.'), search_value: '', diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue index ab9990a323..63ae7e99bb 100644 --- a/erpnext/public/js/hub/pages/FeaturedItems.vue +++ b/erpnext/public/js/hub/pages/FeaturedItems.vue @@ -33,10 +33,8 @@ export default { // Constants page_title: __('Your Featured Items'), - empty_state_message: __(`No featured items yet. Got to your - - Published Items - and feature upto 8 items that you want to highlight to your customers.`) + empty_state_message: __('No featured items yet. Got to your {0} and feature up to eight items that you want to highlight to your customers.', + [`${__("Published Items")}`]) }; }, created() { @@ -71,9 +69,9 @@ export default { const item_name = this.items.filter(item => item.hub_item_name === hub_item_name); - alert = frappe.show_alert(__(`${item_name} removed. - Undo`), - grace_period/1000, + alert_message = __('{0} removed. {1}', [item_name, + `${__('Undo')}`]); + alert = frappe.show_alert(alert_message, grace_period / 1000, { 'undo-remove': undo_remove.bind(this) } diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue index 51ade42cba..93002a7b27 100644 --- a/erpnext/public/js/hub/pages/Item.vue +++ b/erpnext/public/js/hub/pages/Item.vue @@ -113,12 +113,12 @@ export default { let stats = __('No views yet'); if (this.item.view_count) { - const views_message = __(`${this.item.view_count} Views`); + const views_message = __('{0} Views', [this.item.view_count]); const rating_html = get_rating_html(this.item.average_rating); const rating_count = this.item.no_of_ratings > 0 - ? `${this.item.no_of_ratings} reviews` + ? __('{0} reviews', [this.item.no_of_ratings]) : __('No reviews yet'); stats = [views_message, rating_html, rating_count]; @@ -310,7 +310,7 @@ export default { return this.get_item_details(); }) .then(() => { - frappe.show_alert(__(`${this.item.item_name} Updated`)); + frappe.show_alert(__('{0} Updated', [this.item.item_name])); }); }, @@ -337,7 +337,7 @@ export default { }, unpublish_item() { - frappe.confirm(__(`Unpublish {0}?`, [this.item.item_name]), () => { + frappe.confirm(__('Unpublish {0}?', [this.item.item_name]), () => { frappe .call('erpnext.hub_node.api.unpublish_item', { item_code: this.item.item_code, diff --git a/erpnext/public/js/hub/pages/NotFound.vue b/erpnext/public/js/hub/pages/NotFound.vue index 246d31bc68..8901b97802 100644 --- a/erpnext/public/js/hub/pages/NotFound.vue +++ b/erpnext/public/js/hub/pages/NotFound.vue @@ -27,7 +27,7 @@ export default { }, // Constants - empty_state_message: __(`Sorry! I could not find what you were looking for.`) + empty_state_message: __('Sorry! We could not find what you were looking for.') }; }, } diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue index 735f2b92ec..96fa0aae4e 100644 --- a/erpnext/public/js/hub/pages/Publish.vue +++ b/erpnext/public/js/hub/pages/Publish.vue @@ -75,14 +75,11 @@ export default { // TODO: multiline translations don't work page_title: __('Publish Items'), search_placeholder: __('Search Items ...'), - empty_state_message: __(`No Items selected yet. Browse and click on items below to publish.`), - valid_items_instruction: __(`Only items with an image and description can be published. Please update them if an item in your inventory does not appear.`), + empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'), + valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'), last_sync_message: (hub.settings.last_sync_datetime) - ? __(`Last sync was - - ${comment_when(hub.settings.last_sync_datetime)}. - - See your Published Items.`) + ? __('Last sync was {0}.', [`${comment_when(hub.settings.last_sync_datetime)}`]) + + ` ${__('See your Published Items.')}` : '' }; }, @@ -147,11 +144,9 @@ export default { }, add_last_sync_message() { - this.last_sync_message = __(`Last sync was - - ${comment_when(hub.settings.last_sync_datetime)}. - - See your Published Items.`); + this.last_sync_message = __('Last sync was {0}.', + [`${comment_when(hub.settings.last_sync_datetime)}`] + ) + `${__('See your Published Items')}.`; }, clear_last_sync_message() { diff --git a/erpnext/public/js/hub/pages/SavedItems.vue b/erpnext/public/js/hub/pages/SavedItems.vue index c29675acd3..7007ddcf8e 100644 --- a/erpnext/public/js/hub/pages/SavedItems.vue +++ b/erpnext/public/js/hub/pages/SavedItems.vue @@ -29,7 +29,7 @@ export default { // Constants page_title: __('Saved Items'), - empty_state_message: __(`You haven't saved any items yet.`) + empty_state_message: __('You have not saved any items yet.') }; }, created() { @@ -64,8 +64,13 @@ export default { const item_name = this.items.filter(item => item.hub_item_name === hub_item_name); - alert = frappe.show_alert(__(`${item_name} removed. - Undo`), + alert = frappe.show_alert(` + + ${__('{0} removed.', [item_name], 'A specific Item has been removed.')} + + ${__('Undo', None, 'Undo removal of item.')} + + `, grace_period/1000, { 'undo-remove': undo_remove.bind(this) diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue index 103284289b..c10841e984 100644 --- a/erpnext/public/js/hub/pages/Search.vue +++ b/erpnext/public/js/hub/pages/Search.vue @@ -42,7 +42,10 @@ export default { computed: { page_title() { return this.items.length - ? __(`Results for "${this.search_value}" ${this.category !== 'All'? `in category ${this.category}` : ''}`) + ? __('Results for "{0}" {1}', [ + this.search_value, + this.category !== 'All' ? __('in category {0}', [this.category]) : '' + ]) : __('No Items found.'); } }, diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue index e339eaa3e5..c0903c64c3 100644 --- a/erpnext/public/js/hub/pages/Seller.vue +++ b/erpnext/public/js/hub/pages/Seller.vue @@ -136,7 +136,7 @@ export default { this.init = false; this.profile = data.profile; this.items = data.items; - this.item_container_heading = data.is_featured_item? "Features Items":"Popular Items"; + this.item_container_heading = data.is_featured_item ? __('Featured Items') : __('Popular Items'); this.hub_seller = this.items[0].hub_seller; this.recent_seller_reviews = data.recent_seller_reviews; this.seller_product_view_stats = data.seller_product_view_stats; @@ -147,7 +147,7 @@ export default { this.country = __(profile.country); this.site_name = __(profile.site_name); - this.joined_when = __(`Joined ${comment_when(profile.creation)}`); + this.joined_when = __('Joined {0}', [comment_when(profile.creation)]); this.image = profile.logo; this.sections = [ diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 5d21190e37..092f83903e 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -161,7 +161,10 @@ erpnext.setup.slides_settings = [ if(r.message){ exist = r.message; me.get_field("bank_account").set_value(""); - frappe.msgprint(__(`Account ${me.values.bank_account} already exists, enter a different name for your bank account`)); + let message = __('Account {0} already exists. Please enter a different name for your bank account.', + [me.values.bank_account] + ); + frappe.msgprint(message); } } }); From 0508e6bdfaaac6771ec65e4e84fc5798ddf4785e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Nov 2020 09:16:12 +0530 Subject: [PATCH 152/283] fix: Added link of bank reconciliation and clearance in accounting desk page (#23809) --- erpnext/accounts/desk_page/accounting/accounting.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index b2a3f83e5f..a18dbffd9a 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -43,7 +43,7 @@ { "hidden": 0, "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 Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\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]" + "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 Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, From 6b57cf32854bac4fc95e1c27beb67a2494aba4bb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 25 Nov 2020 09:17:16 +0530 Subject: [PATCH 153/283] feat: Quality Inspection on Job Card (#23964) * feat: Quality Inspection on Job Card * fix(Job Card): quality inspection filter query * fix: sider issues --- .../doctype/job_card/job_card.js | 10 +++ .../doctype/job_card/job_card.json | 11 ++- .../quality_inspection/quality_inspection.js | 24 ++++-- .../quality_inspection.json | 4 +- .../quality_inspection/quality_inspection.py | 85 +++++++++++++------ 5 files changed, 96 insertions(+), 38 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index b051b3243f..4e8dd41022 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -31,6 +31,16 @@ frappe.ui.form.on('Job Card', { } } + frm.set_query("quality_inspection", function() { + return { + query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query", + filters: { + "item_code": frm.doc.production_item, + "reference_name": frm.doc.name + } + }; + }); + frm.trigger("toggle_operation_number"); if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 575e719043..5713f697e9 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -20,6 +20,7 @@ "production_item", "item_name", "for_quantity", + "quality_inspection", "wip_warehouse", "column_break_12", "employee", @@ -305,11 +306,19 @@ "label": "Sequence Id", "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal;", + "fieldname": "quality_inspection", + "fieldtype": "Link", + "label": "Quality Inspection", + "no_copy": 1, + "options": "Quality Inspection" } ], "is_submittable": 1, "links": [], - "modified": "2020-10-14 12:58:25.327897", + "modified": "2020-11-19 18:26:50.531664", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 22f29e05b4..376848afaa 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -31,17 +31,27 @@ frappe.ui.form.on("Quality Inspection", { // item code based on GRN/DN cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) { - const doctype = (doc.reference_type == "Stock Entry") ? - "Stock Entry Detail" : doc.reference_type + " Item"; + let doctype = doc.reference_type; + + if (doc.reference_type !== "Job Card") { + doctype = (doc.reference_type == "Stock Entry") ? + "Stock Entry Detail" : doc.reference_type + " Item"; + } if (doc.reference_type && doc.reference_name) { + let filters = { + "from": doctype, + "inspection_type": doc.inspection_type + }; + + if (doc.reference_type == doctype) + filters["reference_name"] = doc.reference_name; + else + filters["parent"] = doc.reference_name; + return { query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query", - filters: { - "from": doctype, - "parent": doc.reference_name, - "inspection_type": doc.inspection_type - } + filters: filters }; } }, diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index dd95075e28..f6d76194d9 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -73,7 +73,7 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", - "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry", + "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry\nJob Card", "reqd": 1 }, { @@ -236,7 +236,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-10-21 13:03:11.938072", + "modified": "2020-11-19 17:06:05.409963", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 399a63a186..ae4eb9b995 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -53,16 +53,28 @@ class QualityInspection(Document): def update_qc_reference(self): quality_inspection = self.name if self.docstatus == 1 else "" - doctype = self.reference_type + ' Item' - if self.reference_type == 'Stock Entry': - doctype = 'Stock Entry Detail' - if self.reference_type and self.reference_name: - frappe.db.sql("""update `tab{child_doc}` t1, `tab{parent_doc}` t2 - set t1.quality_inspection = %s, t2.modified = %s - where t1.parent = %s and t1.item_code = %s and t1.parent = t2.name""" - .format(parent_doc=self.reference_type, child_doc=doctype), - (quality_inspection, self.modified, self.reference_name, self.item_code)) + if self.reference_type == 'Job Card': + if self.reference_name: + frappe.db.sql(""" + UPDATE `tab{doctype}` + SET quality_inspection = %s, modified = %s + WHERE name = %s and production_item = %s + """.format(doctype=self.reference_type), + (quality_inspection, self.modified, self.reference_name, self.item_code)) + + else: + doctype = self.reference_type + ' Item' + if self.reference_type == 'Stock Entry': + doctype = 'Stock Entry Detail' + + if self.reference_type and self.reference_name: + frappe.db.sql(""" + UPDATE `tab{child_doc}` t1, `tab{parent_doc}` t2 + SET t1.quality_inspection = %s, t2.modified = %s + WHERE t1.parent = %s and t1.item_code = %s and t1.parent = t2.name + """.format(parent_doc=self.reference_type, child_doc=doctype), + (quality_inspection, self.modified, self.reference_name, self.item_code)) def set_status_based_on_acceptance_formula(self): for reading in self.readings: @@ -95,27 +107,44 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): mcond = get_match_cond(filters["from"]) cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')" - if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\ - and filters.get("inspection_type") != "In Process": - cond = """and item_code in (select name from `tabItem` where - inspection_required_before_purchase = 1)""" - elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\ - and filters.get("inspection_type") != "In Process": - cond = """and item_code in (select name from `tabItem` where - inspection_required_before_delivery = 1)""" - elif filters.get('from') == 'Stock Entry Detail': - cond = """and s_warehouse is null""" + if filters.get("parent"): + if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\ + and filters.get("inspection_type") != "In Process": + cond = """and item_code in (select name from `tabItem` where + inspection_required_before_purchase = 1)""" + elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\ + and filters.get("inspection_type") != "In Process": + cond = """and item_code in (select name from `tabItem` where + inspection_required_before_delivery = 1)""" + elif filters.get('from') == 'Stock Entry Detail': + cond = """and s_warehouse is null""" - if filters.get('from') in ['Supplier Quotation Item']: - qi_condition = "" + if filters.get('from') in ['Supplier Quotation Item']: + qi_condition = "" - return frappe.db.sql(""" select item_code from `tab{doc}` - where parent=%(parent)s and docstatus < 2 and item_code like %(txt)s - {qi_condition} {cond} {mcond} - order by item_code limit {start}, {page_len}""".format(doc=filters.get('from'), - parent=filters.get('parent'), cond = cond, mcond = mcond, start = start, - page_len = page_len, qi_condition = qi_condition), - {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt}) + return frappe.db.sql(""" + SELECT item_code + FROM `tab{doc}` + WHERE parent=%(parent)s and docstatus < 2 and item_code like %(txt)s + {qi_condition} {cond} {mcond} + ORDER BY item_code limit {start}, {page_len} + """.format(doc=filters.get('from'), + cond = cond, mcond = mcond, start = start, + page_len = page_len, qi_condition = qi_condition), + {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt}) + + elif filters.get("reference_name"): + return frappe.db.sql(""" + SELECT production_item + FROM `tab{doc}` + WHERE name = %(reference_name)s and docstatus < 2 and production_item like %(txt)s + {qi_condition} {cond} {mcond} + ORDER BY production_item + LIMIT {start}, {page_len} + """.format(doc=filters.get("from"), + cond = cond, mcond = mcond, start = start, + page_len = page_len, qi_condition = qi_condition), + {'reference_name': filters.get('reference_name'), 'txt': "%%%s%%" % txt}) @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs From 7824e812980df8e772c4cf0af90c33ad741585a6 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 25 Nov 2020 14:54:50 +0530 Subject: [PATCH 154/283] fix: ignore exception during leave ledger creation from patch --- .../doctype/leave_application/leave_application.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 3f25f58383..e9bcfb3a8b 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -376,24 +376,32 @@ class LeaveApplication(Document): if expiry_date: self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp) else: + raise_exception = True + if frappe.flags.in_patch: + raise_exception=False + args = dict( leaves=self.total_leave_days * -1, from_date=self.from_date, to_date=self.to_date, is_lwp=lwp, - holiday_list=get_holiday_list_for_employee(self.employee) + holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or '' ) create_leave_ledger_entry(self, args, submit) def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp): ''' splits leave application into two ledger entries to consider expiry of allocation ''' + + raise_exception = True + if frappe.flags.in_patch: + raise_exception=False + args = dict( from_date=self.from_date, to_date=expiry_date, leaves=(date_diff(expiry_date, self.from_date) + 1) * -1, is_lwp=lwp, - holiday_list=get_holiday_list_for_employee(self.employee), - + holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or '' ) create_leave_ledger_entry(self, args, submit) From e15b6a91dea531c555f4e60c6929c7740d3a7422 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Wed, 25 Nov 2020 15:36:41 +0530 Subject: [PATCH 155/283] Filters for tax templates (#23998) * feat: add company filter to tax templates * fix: remove filer from PO because it is from tran * fix: linting * fix: solve translation string issues * fix: remove doctype name --- erpnext/accounts/doctype/pos_profile/pos_profile.js | 9 +++++++++ erpnext/public/js/controllers/transaction.js | 11 +++++++++++ erpnext/selling/sales_common.js | 12 +----------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 558e21c13a..7f4f755480 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -35,6 +35,15 @@ frappe.ui.form.on('POS Profile', { }; }); + frm.set_query("taxes_and_charges", function() { + return { + filters: [ + ['Sales Taxes and Charges Template', 'company', '=', frm.doc.company], + ['Sales Taxes and Charges Template', 'docstatus', '!=', 2] + ] + }; + }); + frm.set_query('company_address', function(doc) { if(!doc.company) { frappe.throw(__('Please set Company')); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1358a4bd08..7f08cd1359 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -209,6 +209,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); } + if (this.frm.fields_dict.taxes_and_charges) { + this.frm.set_query("taxes_and_charges", function() { + return { + filters: [ + ['company', '=', me.frm.doc.company], + ['docstatus', '!=', 2] + ] + }; + }); + } + }, onload: function() { var me = this; diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 002cfe41e1..7f00fca8f0 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -42,16 +42,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ me.frm.set_query('customer_address', erpnext.queries.address_query); me.frm.set_query('shipping_address_name', erpnext.queries.address_query); - if(this.frm.fields_dict.taxes_and_charges) { - this.frm.set_query("taxes_and_charges", function() { - return { - filters: [ - ['Sales Taxes and Charges Template', 'company', '=', me.frm.doc.company], - ['Sales Taxes and Charges Template', 'docstatus', '!=', 2] - ] - } - }); - } if(this.frm.fields_dict.selling_price_list) { this.frm.set_query("selling_price_list", function() { @@ -479,7 +469,7 @@ frappe.ui.form.on(cur_frm.doctype,"project", function(frm) { $.each(frm.doc["items"] || [], function(i, row) { if(r.message) { frappe.model.set_value(row.doctype, row.name, "cost_center", r.message); - frappe.msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message)); + frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message])); } }) } From e60a62bde5b59c9d067d220d20aa4346795816ff Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Wed, 25 Nov 2020 15:37:02 +0530 Subject: [PATCH 156/283] fix: function imports in account_balance_timeline.py (#24003) --- .../account_balance_timeline/account_balance_timeline.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index 39bf4b053a..85f54f98ba 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,9 +6,8 @@ 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.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.dashboard import cache_source +from frappe.utils.dateutils import get_from_date_from_timespan, get_period_ending from frappe.utils.nestedset import get_descendants_of @frappe.whitelist() From 90e33e53fd649f5a52461a9403106aab4b89aeed Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Wed, 25 Nov 2020 15:37:54 +0530 Subject: [PATCH 157/283] refactor: Format translation strings (#24004) * fix: translation strings * fix: linting --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 +-- .../doctype/clinical_procedure/clinical_procedure.js | 6 ++---- erpnext/public/js/controllers/buying.js | 3 +-- erpnext/selling/doctype/sales_order/sales_order.js | 3 +-- erpnext/selling/page/point_of_sale/pos_controller.js | 3 +-- erpnext/setup/doctype/sales_person/sales_person.js | 3 +-- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d8394785c6..ab4bfb14ec 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -339,8 +339,7 @@ class JournalEntry(AccountsController): currency=account_currency) if flt(voucher_total) < (flt(order.advance_paid) + total): - frappe.throw(_("Advance paid against {0} {1} cannot be greater \ - than Grand Total {2}").format(reference_type, reference_name, formatted_voucher_total)) + frappe.throw(_("Advance paid against {0} {1} cannot be greater than Grand Total {2}").format(reference_type, reference_name, formatted_voucher_total)) def validate_invoices(self): """Validate totals and docstatus for invoices""" diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index eb7d4bdeba..1d4411d73d 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -85,8 +85,7 @@ frappe.ui.form.on('Clinical Procedure', { callback: function(r) { if (r.message) { frappe.show_alert({ - message: __('Stock Entry {0} created', - ['' + r.message + '']), + message: __('Stock Entry {0} created', ['' + r.message + '']), indicator: 'green' }); } @@ -105,8 +104,7 @@ frappe.ui.form.on('Clinical Procedure', { callback: function(r) { if (!r.exc) { if (r.message == 'insufficient stock') { - let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?', - [frm.doc.warehouse.bold()]); + let msg = __('Stock quantity to start the Procedure is not available in the Warehouse {0}. Do you want to record a Stock Entry?', [frm.doc.warehouse.bold()]); frappe.confirm( msg, function() { diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 58ac38f0a8..3f5652aa5d 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -218,8 +218,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ var is_negative_qty = false; for(var i = 0; i%(name)s', {name:d}) }).join(', ')]), indicator: 'green' diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 970d840665..ad1633e71d 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -644,8 +644,7 @@ erpnext.PointOfSale.Controller = class { }) } else if (available_qty < qty_needed) { frappe.show_alert({ - message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', - [bold_item_code, bold_warehouse, bold_available_qty]), + message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]), indicator: 'orange' }); frappe.utils.play_sound("error"); diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 8f7593d6ee..b71a92f8a9 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -5,8 +5,7 @@ frappe.ui.form.on('Sales Person', { refresh: function(frm) { if(frm.doc.__onload && frm.doc.__onload.dashboard_info) { var info = frm.doc.__onload.dashboard_info; - frm.dashboard.add_indicator(__('Total Contribution Amount: {0}', - [format_currency(info.allocated_amount, info.currency)]), 'blue'); + frm.dashboard.add_indicator(__('Total Contribution Amount: {0}', [format_currency(info.allocated_amount, info.currency)]), 'blue'); } }, From f32cff1080f9412ed27a843d1f573021d56d5db5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 25 Nov 2020 16:00:15 +0530 Subject: [PATCH 158/283] feat : Leave type with partial payment (#23173) * feat: Partially paid Leaves * feat: some importatnt validation * fix: requested changes * fix: requested changes * fix: travis, sider, codacy * fix: changes requested * test: Partially Paid Leaves --- erpnext/hr/doctype/employee/employee.json | 4 +- .../leave_application/leave_application.py | 6 +- erpnext/hr/doctype/leave_type/leave_type.json | 19 +++++- erpnext/hr/doctype/leave_type/leave_type.py | 6 ++ .../hr/doctype/leave_type/test_leave_type.py | 5 ++ .../doctype/salary_slip/salary_slip.js | 42 ++++++------ .../doctype/salary_slip/salary_slip.py | 64 +++++++++++++------ .../doctype/salary_slip/test_salary_slip.py | 19 +++++- 8 files changed, 117 insertions(+), 48 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index da789198e5..4cabe97cc4 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -672,10 +672,10 @@ "oldfieldtype": "Date" }, { - "depends_on": "eval:doc.status == \"Left\"", "fieldname": "relieving_date", "fieldtype": "Date", "label": "Relieving Date", + "mandatory_depends_on": "eval:doc.status == \"Left\"", "oldfieldname": "relieving_date", "oldfieldtype": "Date" }, @@ -822,7 +822,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2020-10-06 15:58:23.805489", + "modified": "2020-10-16 14:41:10.580897", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 3f25f58383..ca79dff115 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -130,8 +130,7 @@ class LeaveApplication(Document): if self.status == "Approved": 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" - + status = "Half Day" if self.half_day_date and getdate(date) == getdate(self.half_day_date) else "On Leave" attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -293,7 +292,8 @@ class LeaveApplication(Document): 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 - elif self.half_day == 0: + + if self.half_day == 0: self.half_day_date = None def notify_employee(self): diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 0af832f903..4a135e0ffe 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -15,6 +15,8 @@ "column_break_3", "is_carry_forward", "is_lwp", + "is_ppl", + "fraction_of_daily_salary_per_leave", "is_optional_leave", "allow_negative", "include_holiday", @@ -77,6 +79,7 @@ }, { "default": "0", + "depends_on": "eval:doc.is_ppl == 0", "fieldname": "is_lwp", "fieldtype": "Check", "label": "Is Leave Without Pay" @@ -183,12 +186,26 @@ { "fieldname": "column_break_22", "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.is_lwp == 0", + "fieldname": "is_ppl", + "fieldtype": "Check", + "label": "Is Partially Paid Leave" + }, + { + "depends_on": "eval:doc.is_ppl == 1", + "fieldname": "fraction_of_daily_salary_per_leave", + "fieldtype": "Float", + "label": "Fraction of Daily Salary per Leave", + "mandatory_depends_on": "eval:doc.is_ppl == 1" } ], "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2019-12-12 12:48:37.780254", + "modified": "2020-08-26 14:04:54.318687", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index c0d1296841..21f180b857 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -21,3 +21,9 @@ class LeaveType(Document): leave_allocation = [l['name'] for l in leave_allocation] if leave_allocation: frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec + + if self.is_lwp and self.is_ppl: + frappe.throw(_("Leave Type can be either without pay or partial pay")) + + if self.is_ppl and (self.fraction_of_daily_salary_per_leave < 0 or self.fraction_of_daily_salary_per_leave > 1): + frappe.throw(_("The fraction of Daily Salary per Leave should be between 0 and 1")) diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index 0c4f435860..7fef2975c8 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -18,9 +18,14 @@ def create_leave_type(**args): "allow_encashment": args.allow_encashment or 0, "is_earned_leave": args.is_earned_leave or 0, "is_lwp": args.is_lwp or 0, + "is_ppl":args.is_ppl or 0, "is_carry_forward": args.is_carry_forward or 0, "expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0, "encashment_threshold_days": args.encashment_threshold_days or 5, "earning_component": "Leave Encashment" }) + + if leave_type.is_ppl: + leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5 + return leave_type \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index 7b69dbe8d6..0671b570d1 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -13,12 +13,12 @@ frappe.ui.form.on("Salary Slip", { ]; }); - frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){ + frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function() { return { filters: { employee: frm.doc.employee } - } + }; }; frm.set_query("salary_component", "earnings", function() { @@ -26,7 +26,7 @@ frappe.ui.form.on("Salary Slip", { filters: { type: "earning" } - } + }; }); frm.set_query("salary_component", "deductions", function() { @@ -34,18 +34,18 @@ frappe.ui.form.on("Salary Slip", { filters: { type: "deduction" } - } + }; }); frm.set_query("employee", function() { - return{ + return { query: "erpnext.controllers.queries.employee_query" - } + }; }); }, - start_date: function(frm){ - if(frm.doc.start_date){ + start_date: function(frm) { + if (frm.doc.start_date) { frm.trigger("set_end_date"); } }, @@ -54,7 +54,7 @@ frappe.ui.form.on("Salary Slip", { frm.events.get_emp_and_working_day_details(frm); }, - set_end_date: function(frm){ + set_end_date: function(frm) { frappe.call({ method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', args: { @@ -66,22 +66,22 @@ frappe.ui.form.on("Salary Slip", { frm.set_value('end_date', r.message.end_date); } } - }) + }); }, company: function(frm) { var company = locals[':Company'][frm.doc.company]; - if(!frm.doc.letter_head && company.default_letter_head) { + if (!frm.doc.letter_head && company.default_letter_head) { frm.set_value('letter_head', company.default_letter_head); } }, refresh: function(frm) { - frm.trigger("toggle_fields") + frm.trigger("toggle_fields"); var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"]; - cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false); - cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false); + cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false); + cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false); }, salary_slip_based_on_timesheet: function(frm) { @@ -98,12 +98,12 @@ frappe.ui.form.on("Salary Slip", { frm.events.get_emp_and_working_day_details(frm); }, - leave_without_pay: function(frm){ + leave_without_pay: function(frm) { if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) { return frappe.call({ method: 'process_salary_based_on_working_days', doc: frm.doc, - callback: function(r, rt) { + callback: function() { frm.refresh(); } }); @@ -121,10 +121,10 @@ frappe.ui.form.on("Salary Slip", { return frappe.call({ method: 'get_emp_and_working_day_details', doc: frm.doc, - callback: function(r, rt) { + callback: function(r) { frm.refresh(); - if (r.message){ - frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)); + if (r.message[1] !== "Leave" && r.message[0]) { + frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as ")+ r.message[0] +__(". You can can change this in ") + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)); } } }); @@ -141,7 +141,7 @@ frappe.ui.form.on('Salary Slip Timesheet', { }); // calculate total working hours, earnings based on hourly wages and totals -var total_work_hours = function(frm, dt, dn) { +var total_work_hours = function(frm) { var total_working_hours = 0.0; $.each(frm.doc["timesheets"] || [], function(i, timesheet) { total_working_hours += timesheet.working_hours; @@ -165,4 +165,4 @@ var total_work_hours = function(frm, dt, dn) { frm.doc.rounded_total = Math.round(frm.doc.net_pay); refresh_many(['net_pay', 'rounded_total']); }); -} +}; diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index cecb8cde7c..7b87ae5e7b 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -136,8 +136,8 @@ class SalarySlip(TransactionBase): self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0 self.set_time_sheet() self.pull_sal_struct() - consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" - return consider_unmarked_attendance_as + payroll_based_on, consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"]) + return [payroll_based_on, consider_unmarked_attendance_as] def set_time_sheet(self): if self.salary_slip_based_on_timesheet: @@ -210,10 +210,10 @@ class SalarySlip(TransactionBase): frappe.throw(_("Please set Payroll based on in Payroll settings")) if payroll_based_on == "Attendance": - actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays) + actual_lwp, absent = self.calculate_lwp_ppl_and_absent_days_based_on_attendance(holidays) self.absent_days = absent else: - actual_lwp = self.calculate_lwp_based_on_leave_application(holidays, working_days) + actual_lwp = self.calculate_lwp_or_ppl_based_on_leave_application(holidays, working_days) if not lwp: lwp = actual_lwp @@ -300,7 +300,7 @@ class SalarySlip(TransactionBase): return holidays - def calculate_lwp_based_on_leave_application(self, holidays, working_days): + def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days): lwp = 0 holidays = "','".join(holidays) daily_wages_fraction_for_half_day = \ @@ -311,10 +311,12 @@ class SalarySlip(TransactionBase): leave = frappe.db.sql(""" SELECT t1.name, CASE WHEN (t1.half_day_date = %(dt)s or t1.to_date = t1.from_date) - THEN t1.half_day else 0 END + THEN t1.half_day else 0 END, + t2.is_ppl, + t2.fraction_of_daily_salary_per_leave FROM `tabLeave Application` t1, `tabLeave Type` t2 WHERE t2.name = t1.leave_type - AND t2.is_lwp = 1 + AND (t2.is_lwp = 1 or t2.is_ppl = 1) AND t1.docstatus = 1 AND t1.employee = %(employee)s AND ifnull(t1.salary_slip, '') = '' @@ -327,19 +329,35 @@ class SalarySlip(TransactionBase): """.format(holidays), {"employee": self.employee, "dt": dt}) if leave: + equivalent_lwp_count = 0 is_half_day_leave = cint(leave[0][1]) - lwp += (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 + is_partially_paid_leave = cint(leave[0][2]) + fraction_of_daily_salary_per_leave = flt(leave[0][3]) + + equivalent_lwp_count = (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 + + if is_partially_paid_leave: + equivalent_lwp_count *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + + lwp += equivalent_lwp_count return lwp - def calculate_lwp_and_absent_days_based_on_attendance(self, holidays): + def calculate_lwp_ppl_and_absent_days_based_on_attendance(self, holidays): lwp = 0 absent = 0 daily_wages_fraction_for_half_day = \ flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 - lwp_leave_types = dict(frappe.get_all("Leave Type", {"is_lwp": 1}, ["name", "include_holiday"], as_list=1)) + leave_types = frappe.get_all("Leave Type", + or_filters=[["is_ppl", "=", 1], ["is_lwp", "=", 1]], + fields =["name", "is_lwp", "is_ppl", "fraction_of_daily_salary_per_leave", "include_holiday"]) + + leave_type_map = {} + for leave_type in leave_types: + leave_type_map[leave_type.name] = leave_type + attendances = frappe.db.sql(''' SELECT attendance_date, status, leave_type FROM `tabAttendance` @@ -351,21 +369,30 @@ class SalarySlip(TransactionBase): ''', values=(self.employee, self.start_date, self.end_date), as_dict=1) for d in attendances: - if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in lwp_leave_types: + if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in leave_type_map.keys(): continue if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays: if d.status == "Absent" or \ - (d.leave_type and d.leave_type in lwp_leave_types and not lwp_leave_types[d.leave_type]): + (d.leave_type and d.leave_type in leave_type_map.keys() and not leave_type_map[d.leave_type]['include_holiday']): continue + if d.leave_type: + fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type]["fraction_of_daily_salary_per_leave"] + if d.status == "Half Day": - lwp += (1 - daily_wages_fraction_for_half_day) - elif d.status == "On Leave" and d.leave_type in lwp_leave_types: - lwp += 1 + equivalent_lwp = (1 - daily_wages_fraction_for_half_day) + + if d.leave_type in leave_type_map.keys() and leave_type_map[d.leave_type]["is_ppl"]: + equivalent_lwp *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + lwp += equivalent_lwp + elif d.status == "On Leave" and d.leave_type and d.leave_type in leave_type_map.keys(): + equivalent_lwp = 1 + if leave_type_map[d.leave_type]["is_ppl"]: + equivalent_lwp *= fraction_of_daily_salary_per_leave if fraction_of_daily_salary_per_leave else 1 + lwp += equivalent_lwp elif d.status == "Absent": absent += 1 - return lwp, absent def add_earning_for_hourly_wages(self, doc, salary_component, amount): @@ -949,9 +976,8 @@ class SalarySlip(TransactionBase): amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") total_amount = amounts['interest_amount'] + amounts['payable_principal_amount'] if payment.total_payment > total_amount: - frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2} - against loan {3}""").format(payment.idx, frappe.bold(payment.total_payment), - frappe.bold(total_amount), frappe.bold(payment.loan))) + frappe.throw(_("Row {0}: Paid amount {1} is greater than pending accrued amount {2}against loan {3}").format( + payment.idx, frappe.bold(payment.total_payment),frappe.bold(total_amount), frappe.bold(payment.loan))) self.total_interest_amount += payment.interest_amount self.total_principal_amount += payment.principal_amount diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 7fe4165362..e08dc7c9c8 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -13,6 +13,8 @@ from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_ from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation +from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ import create_payroll_period, create_exemption_category @@ -93,14 +95,27 @@ class TestSalarySlip(unittest.TestCase): make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay") + leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl = 1) + leave_type_ppl.save() + + alloc = create_leave_allocation( + employee = emp_id, from_date = add_days(first_sunday, 4), + to_date = add_days(first_sunday, 10), new_leaves_allocated = 3, + leave_type = "Test Partially Paid Leave") + alloc.save() + alloc.submit() + + #two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp + make_leave_application(emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave") + ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly") - self.assertEqual(ss.leave_without_pay, 3) + self.assertEqual(ss.leave_without_pay, 4) days_in_month = no_of_days[0] no_of_holidays = no_of_days[1] - self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 3) + self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 4) #Gross pay calculation based on attendances gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) From 755b773616cb7e037e2034d2bc0e396f36580a3f Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 25 Nov 2020 16:05:17 +0530 Subject: [PATCH 159/283] feat: Leave policy assignment (#23112) * feat: Leave Policy Assignment * feat: linking with leave allocation and valiations * style: removed old code from leave period * feat: Bulk Leave policy Assignment and grant Leaves * fix: overlap validation * feat: earned leaves based on joining date * feat: automatic grant leave based on leave policy * patch: create leave policy assignment based on employee current leave policy * fix: dependent test cases * test: Leave policy assignment * fix: some enhancement * style: break large function into small function * fix:requested Changes * fix(patch): Handled old Leave allocatioln * fix:codacy * fix: travis and sider,codacy * fix: codacy * fix: codacy * fix: requested changes and sider Co-authored-by: Nabin Hait --- erpnext/hooks.py | 4 +- erpnext/hr/doctype/employee/employee.json | 11 +- .../employee_grade/employee_grade.json | 130 ++------------ .../hr/doctype/hr_settings/hr_settings.json | 13 +- .../leave_allocation/leave_allocation.json | 16 +- .../leave_allocation/leave_allocation.py | 10 ++ .../test_leave_application.py | 37 ++-- .../leave_encashment/test_leave_encashment.py | 18 +- .../hr/doctype/leave_period/leave_period.js | 78 +-------- .../hr/doctype/leave_period/leave_period.py | 109 +----------- .../doctype/leave_period/test_leave_period.py | 34 +--- .../leave_policy_assignment/__init__.py | 0 .../leave_policy_assignment.js | 72 ++++++++ .../leave_policy_assignment.json | 160 +++++++++++++++++ .../leave_policy_assignment.py | 163 ++++++++++++++++++ .../leave_policy_assignment_list.js | 138 +++++++++++++++ .../test_leave_policy_assignment.py | 103 +++++++++++ erpnext/hr/doctype/leave_type/leave_type.json | 10 +- erpnext/hr/utils.py | 129 ++++++++------ erpnext/patches.txt | 1 + ..._based_on_employee_current_leave_policy.py | 77 +++++++++ 21 files changed, 899 insertions(+), 414 deletions(-) create mode 100644 erpnext/hr/doctype/leave_policy_assignment/__init__.py create mode 100644 erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js create mode 100644 erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json create mode 100644 erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py create mode 100644 erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js create mode 100644 erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py create mode 100644 erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 741176f33f..726ab6e22a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -347,14 +347,16 @@ scheduler_events = { "erpnext.setup.doctype.email_digest.email_digest.send", "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms", "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", + "erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.automatically_allocate_leaves_based_on_leave_policy", "erpnext.hr.utils.generate_leave_encashment", + "erpnext.hr.utils.allocate_earned_leaves", + "erpnext.hr.utils.grant_leaves_automatically", "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.create_process_loan_security_shortfall", "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans", "erpnext.crm.doctype.lead.lead.daily_open_lead" ], "monthly_long": [ "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" ] } diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 4cabe97cc4..4f1c04ff5d 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -57,7 +57,6 @@ "column_break_45", "shift_request_approver", "attendance_and_leave_details", - "leave_policy", "attendance_device_id", "column_break_44", "holiday_list", @@ -411,14 +410,6 @@ "oldfieldtype": "Link", "options": "Branch" }, - { - "fetch_from": "grade.default_leave_policy", - "fetch_if_empty": 1, - "fieldname": "leave_policy", - "fieldtype": "Link", - "label": "Leave Policy", - "options": "Leave Policy" - }, { "description": "Applicable Holiday List", "fieldname": "holiday_list", @@ -822,7 +813,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2020-10-16 14:41:10.580897", + "modified": "2020-10-16 15:02:04.283657", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/employee_grade/employee_grade.json b/erpnext/hr/doctype/employee_grade/employee_grade.json index e63ffae0c4..88b061a3c3 100644 --- a/erpnext/hr/doctype/employee_grade/employee_grade.json +++ b/erpnext/hr/doctype/employee_grade/employee_grade.json @@ -1,167 +1,69 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 16:14:24.174138", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 16:14:24.174138", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "default_salary_structure" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_leave_policy", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Leave Policy", - "length": 0, - "no_copy": 0, - "options": "Leave Policy", - "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_salary_structure", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Salary Structure", - "length": 0, - "no_copy": 0, - "options": "Salary Structure", - "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": "Salary Structure" } ], - "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-09-18 17:17:45.617624", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-08-26 13:12:07.815330", "modified_by": "Administrator", "module": "HR", "name": "Employee Grade", - "name_case": "", "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": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "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": "HR Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "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": "HR User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 4374d2911a..f99963504a 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -21,6 +21,7 @@ "show_leaves_of_all_department_members_in_calendar", "auto_leave_encashment", "restrict_backdated_leave_application", + "automatically_allocate_leaves_based_on_leave_policy", "hiring_settings", "check_vacancies" ], @@ -41,7 +42,7 @@ "description": "Employee records are created using the selected field", "fieldname": "emp_created_by", "fieldtype": "Select", - "label": "Employee Records to Be Created By", + "label": "Employee Records to be created by", "options": "Naming Series\nEmployee Number\nFull Name" }, { @@ -117,7 +118,7 @@ "default": "0", "fieldname": "restrict_backdated_leave_application", "fieldtype": "Check", - "label": "Restrict Backdated Leave Applications" + "label": "Restrict Backdated Leave Application" }, { "depends_on": "eval:doc.restrict_backdated_leave_application == 1", @@ -125,13 +126,19 @@ "fieldtype": "Link", "label": "Role Allowed to Create Backdated Leave Application", "options": "Role" + }, + { + "default": "0", + "fieldname": "automatically_allocate_leaves_based_on_leave_policy", + "fieldtype": "Check", + "label": "Automatically Allocate Leaves Based On Leave Policy" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2020-10-13 11:49:46.168027", + "modified": "2020-08-27 14:30:28.995324", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 007497e34a..4b315014da 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-02-20 19:10:38", @@ -24,6 +25,7 @@ "compensatory_request", "leave_period", "leave_policy", + "leave_policy_assignment", "carry_forwarded_leaves_count", "expired", "amended_from", @@ -160,9 +162,10 @@ "read_only": 1 }, { - "fetch_from": "employee.leave_policy", + "fetch_from": "leave_policy_assignment.leave_policy", "fieldname": "leave_policy", "fieldtype": "Link", + "hidden": 1, "in_standard_filter": 1, "label": "Leave Policy", "options": "Leave Policy", @@ -209,12 +212,21 @@ "fieldtype": "Float", "label": "Carry Forwarded Leaves", "read_only": 1 + }, + { + "fieldname": "leave_policy_assignment", + "fieldtype": "Link", + "label": "Leave Policy Assignment", + "options": "Leave Policy Assignment", + "read_only": 1 } ], "icon": "fa fa-ok", "idx": 1, + "index_web_pages_for_search": 1, "is_submittable": 1, - "modified": "2019-08-08 15:08:42.440909", + "links": [], + "modified": "2020-08-20 14:25:10.314323", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 03fe3fa035..a09cd2ea11 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -51,9 +51,19 @@ class LeaveAllocation(Document): def on_cancel(self): self.create_leave_ledger_entry(submit=False) + if self.leave_policy_assignment: + self.update_leave_policy_assignments_when_no_allocations_left() if self.carry_forward: self.set_carry_forwarded_leaves_in_previous_allocation(on_cancel=True) + def update_leave_policy_assignments_when_no_allocations_left(self): + allocations = frappe.db.get_list("Leave Allocation", filters = { + "docstatus": 1, + "leave_policy_assignment": self.leave_policy_assignment + }) + if len(allocations) == 0: + frappe.db.set_value("Leave Policy Assignment", self.leave_policy_assignment ,"leaves_allocated", 0) + def validate_period(self): if date_diff(self.to_date, self.from_date) <= 0: frappe.throw(_("To date cannot be before from date")) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 6e909c3f01..53b7a39e51 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -10,6 +10,7 @@ from frappe.permissions import clear_user_permissions_for_doctype from frappe.utils import add_days, nowdate, now_datetime, getdate, add_months from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation +from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees test_dependencies = ["Leave Allocation", "Leave Block List"] @@ -410,25 +411,39 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21) def test_earned_leaves_creation(self): + + frappe.db.sql('''delete from `tabLeave Period`''') + frappe.db.sql('''delete from `tabLeave Policy Assignment`''') + frappe.db.sql('''delete from `tabLeave Allocation`''') + frappe.db.sql('''delete from `tabLeave Ledger Entry`''') + leave_period = get_leave_period() employee = get_employee() leave_type = 'Test Earned Leave Type' - if not frappe.db.exists('Leave Type', leave_type): - frappe.get_doc(dict( - leave_type_name = leave_type, - doctype = 'Leave Type', - is_earned_leave = 1, - earned_leave_frequency = 'Monthly', - rounding = 0.5, - max_leaves_allowed = 6 - )).insert() + frappe.delete_doc_if_exists("Leave Type", 'Test Earned Leave Type', force=1) + frappe.get_doc(dict( + leave_type_name = leave_type, + doctype = 'Leave Type', + is_earned_leave = 1, + earned_leave_frequency = 'Monthly', + rounding = 0.5, + max_leaves_allowed = 6 + )).insert() + leave_policy = frappe.get_doc({ "doctype": "Leave Policy", "leave_policy_details": [{"leave_type": leave_type, "annual_allocation": 6}] }).insert() - frappe.db.set_value("Employee", employee.name, "leave_policy", leave_policy.name) - allocate_leaves(employee, leave_period, leave_type, 0, eligible_leaves = 12) + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name + } + + leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) + + frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]).grant_leave_alloc_for_employee() from erpnext.hr.utils import allocate_earned_leaves i = 0 diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index 99f6463416..bbee18bb0a 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -9,6 +9,7 @@ from frappe.utils import today, add_months from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period +from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy\ test_dependencies = ["Leave Type"] @@ -16,6 +17,7 @@ test_dependencies = ["Leave Type"] class TestLeaveEncashment(unittest.TestCase): def setUp(self): frappe.db.sql('''delete from `tabLeave Period`''') + frappe.db.sql('''delete from `tabLeave Policy Assignment`''') frappe.db.sql('''delete from `tabLeave Allocation`''') frappe.db.sql('''delete from `tabLeave Ledger Entry`''') frappe.db.sql('''delete from `tabAdditional Salary`''') @@ -29,14 +31,22 @@ class TestLeaveEncashment(unittest.TestCase): # create employee, salary structure and assignment self.employee = make_employee("test_employee_encashment@example.com") - frappe.db.set_value("Employee", self.employee, "leave_policy", leave_policy.name) + self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": self.leave_period.name + } + + leave_policy_assignments = create_assignment_for_multiple_employees([self.employee], frappe._dict(data)) salary_structure = make_salary_structure("Salary Structure for Encashment", "Monthly", self.employee, other_details={"leave_encashment_amount_per_day": 50}) - # create the leave period and assign the leaves - self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) - self.leave_period.grant_leave_allocation(employee=self.employee) + #grant Leaves + frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]).grant_leave_alloc_for_employee() + def test_leave_balance_value_and_amount(self): frappe.db.sql('''delete from `tabLeave Encashment`''') diff --git a/erpnext/hr/doctype/leave_period/leave_period.js b/erpnext/hr/doctype/leave_period/leave_period.js index bad2b8766c..0e88bc1671 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.js +++ b/erpnext/hr/doctype/leave_period/leave_period.js @@ -2,14 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('Leave Period', { - refresh: (frm)=>{ - frm.set_df_property("grant_leaves", "hidden", frm.doc.__islocal ? 1:0); - if(!frm.is_new()) { - frm.add_custom_button(__('Grant Leaves'), function () { - frm.trigger("grant_leaves"); - }); - } - }, from_date: (frm)=>{ if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); @@ -22,73 +14,7 @@ frappe.ui.form.on('Leave Period', { "filters": { "company": frm.doc.company, } - } - }) - }, - grant_leaves: function(frm) { - var d = new frappe.ui.Dialog({ - title: __('Grant Leaves'), - fields: [ - { - "label": "Filter Employees By (Optional)", - "fieldname": "sec_break", - "fieldtype": "Section Break", - }, - { - "label": "Employee Grade", - "fieldname": "grade", - "fieldtype": "Link", - "options": "Employee Grade" - }, - { - "label": "Department", - "fieldname": "department", - "fieldtype": "Link", - "options": "Department" - }, - { - "fieldname": "col_break", - "fieldtype": "Column Break", - }, - { - "label": "Designation", - "fieldname": "designation", - "fieldtype": "Link", - "options": "Designation" - }, - { - "label": "Employee", - "fieldname": "employee", - "fieldtype": "Link", - "options": "Employee" - }, - { - "fieldname": "sec_break", - "fieldtype": "Section Break", - }, - { - "label": "Add unused leaves from previous allocations", - "fieldname": "carry_forward", - "fieldtype": "Check" - } - ], - primary_action: function() { - var data = d.get_values(); - - frappe.call({ - doc: frm.doc, - method: "grant_leave_allocation", - args: data, - callback: function(r) { - if(!r.exc) { - d.hide(); - frm.reload_doc(); - } - } - }); - }, - primary_action_label: __('Grant') + }; }); - d.show(); - } + }, }); diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 0973ac7198..28a33f6fac 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -7,24 +7,10 @@ import frappe from frappe import _ from frappe.utils import getdate, cstr, add_days, date_diff, getdate, ceil from frappe.model.document import Document -from erpnext.hr.utils import validate_overlap, get_employee_leave_policy +from erpnext.hr.utils import validate_overlap from frappe.utils.background_jobs import enqueue -from six import iteritems class LeavePeriod(Document): - def get_employees(self, args): - conditions, values = [], [] - for field, value in iteritems(args): - if value: - conditions.append("{0}=%s".format(field)) - values.append(value) - - condition_str = " and " + " and ".join(conditions) if len(conditions) else "" - - employees = frappe._dict(frappe.db.sql("select name, date_of_joining from tabEmployee where status='Active' {condition}" #nosec - .format(condition=condition_str), tuple(values))) - - return employees def validate(self): self.validate_dates() @@ -33,96 +19,3 @@ class LeavePeriod(Document): def validate_dates(self): if getdate(self.from_date) >= getdate(self.to_date): frappe.throw(_("To date can not be equal or less than from date")) - - - def grant_leave_allocation(self, grade=None, department=None, designation=None, - employee=None, carry_forward=0): - employee_records = self.get_employees({ - "grade": grade, - "department": department, - "designation": designation, - "name": employee - }) - - if employee_records: - if len(employee_records) > 20: - frappe.enqueue(grant_leave_alloc_for_employees, timeout=600, - employee_records=employee_records, leave_period=self, carry_forward=carry_forward) - else: - grant_leave_alloc_for_employees(employee_records, self, carry_forward) - else: - frappe.msgprint(_("No Employee Found")) - -def grant_leave_alloc_for_employees(employee_records, leave_period, carry_forward=0): - leave_allocations = [] - existing_allocations_for = get_existing_allocations(list(employee_records.keys()), leave_period.name) - leave_type_details = get_leave_type_details() - count = 0 - for employee in employee_records.keys(): - if employee in existing_allocations_for: - continue - count +=1 - leave_policy = get_employee_leave_policy(employee) - if leave_policy: - for leave_policy_detail in leave_policy.leave_policy_details: - if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp: - leave_allocation = create_leave_allocation(employee, leave_policy_detail.leave_type, - leave_policy_detail.annual_allocation, leave_type_details, leave_period, carry_forward, employee_records.get(employee)) - leave_allocations.append(leave_allocation) - frappe.db.commit() - frappe.publish_progress(count*100/len(set(employee_records.keys()) - set(existing_allocations_for)), title = _("Allocating leaves...")) - - if leave_allocations: - frappe.msgprint(_("Leaves has been granted sucessfully")) - -def get_existing_allocations(employees, leave_period): - leave_allocations = frappe.db.sql_list(""" - SELECT DISTINCT - employee - FROM `tabLeave Allocation` - WHERE - leave_period=%s - AND employee in (%s) - AND carry_forward=0 - AND docstatus=1 - """ % ('%s', ', '.join(['%s']*len(employees))), [leave_period] + employees) - if leave_allocations: - frappe.msgprint(_("Skipping Leave Allocation for the following employees, as Leave Allocation records already exists against them. {0}") - .format("\n".join(leave_allocations))) - return leave_allocations - -def get_leave_type_details(): - leave_type_details = frappe._dict() - leave_types = frappe.get_all("Leave Type", - fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carry_forwarded_leaves_after_days"]) - for d in leave_types: - leave_type_details.setdefault(d.name, d) - return leave_type_details - -def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_type_details, leave_period, carry_forward, date_of_joining): - ''' Creates leave allocation for the given employee in the provided leave period ''' - if carry_forward and not leave_type_details.get(leave_type).is_carry_forward: - carry_forward = 0 - - # Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period - if getdate(date_of_joining) > getdate(leave_period.from_date): - remaining_period = ((date_diff(leave_period.to_date, date_of_joining) + 1) / (date_diff(leave_period.to_date, leave_period.from_date) + 1)) - new_leaves_allocated = ceil(new_leaves_allocated * remaining_period) - - # Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0 - if leave_type_details.get(leave_type).is_earned_leave == 1 or leave_type_details.get(leave_type).is_compensatory == 1: - new_leaves_allocated = 0 - - allocation = frappe.get_doc(dict( - doctype="Leave Allocation", - employee=employee, - leave_type=leave_type, - from_date=leave_period.from_date, - to_date=leave_period.to_date, - new_leaves_allocated=new_leaves_allocated, - leave_period=leave_period.name, - carry_forward=carry_forward - )) - allocation.save(ignore_permissions = True) - allocation.submit() - return allocation.name \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py index 1762cf917a..b5857bcd8f 100644 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ b/erpnext/hr/doctype/leave_period/test_leave_period.py @@ -5,43 +5,11 @@ from __future__ import unicode_literals import frappe, erpnext import unittest -from frappe.utils import today, add_months -from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on test_dependencies = ["Employee", "Leave Type", "Leave Policy"] class TestLeavePeriod(unittest.TestCase): - def setUp(self): - frappe.db.sql("delete from `tabLeave Period`") - - def test_leave_grant(self): - leave_type = "_Test Leave Type" - - # create the leave policy - leave_policy = frappe.get_doc({ - "doctype": "Leave Policy", - "leave_policy_details": [{ - "leave_type": leave_type, - "annual_allocation": 20 - }] - }).insert() - leave_policy.submit() - - # create employee and assign the leave period - employee = "test_leave_period@employee.com" - employee_doc_name = make_employee(employee) - frappe.db.set_value("Employee", employee_doc_name, "leave_policy", leave_policy.name) - - # clear the already allocated leave - frappe.db.sql('''delete from `tabLeave Allocation` where employee=%s''', "test_leave_period@employee.com") - - # create the leave period - leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) - - # test leave_allocation - leave_period.grant_leave_allocation(employee=employee_doc_name) - self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20) + pass def create_leave_period(from_date, to_date, company=None): leave_period = frappe.db.get_value('Leave Period', diff --git a/erpnext/hr/doctype/leave_policy_assignment/__init__.py b/erpnext/hr/doctype/leave_policy_assignment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js new file mode 100644 index 0000000000..7c32a0dde0 --- /dev/null +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.js @@ -0,0 +1,72 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Policy Assignment', { + onload: function(frm) { + frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; + }, + + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.leaves_allocated === 0) { + frm.add_custom_button(__("Grant Leave"), function() { + + frappe.call({ + doc: frm.doc, + method: "grant_leave_alloc_for_employee", + callback: function(r) { + let leave_allocations = r.message; + let msg = frm.events.get_success_message(leave_allocations); + frappe.msgprint(msg); + cur_frm.refresh(); + } + }); + }); + } + }, + + get_success_message: function(leave_allocations) { + let msg = __("Leaves has been granted successfully"); + msg += "
{%= report_columns[0].label %}{%= report_columns[1].label %}
${__("Course")}${__("Date")}
${c.name}
${c.name} ${c.schedule_date}
{% if(s.assessment_details) { %} - {{s.assessment_details[c.assessment_criteria][1]}} + {{s.assessment_details[c.assessment_criteria][1]}} {% } %} - + diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 73cc0b836e..96d265c989 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -328,7 +328,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( frappe.msgprint({ message: __('Work Orders Created: {0}', [r.message.map(function(d) { - return repl('%(name)s', {name:d}) + return repl('%(name)s', {name:d}) }).join(', ')]), indicator: 'green' }) @@ -437,7 +437,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( callback: function(r) { if(r.message) { frappe.msgprint(__('Material Request {0} submitted.', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); } d.hide(); me.frm.reload_doc(); diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 970d840665..288084d245 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -45,7 +45,7 @@ erpnext.PointOfSale.Controller = class { { fieldname: "opening_amount", fieldtype: "Currency", in_list_view: 1, label: "Opening Amount", - options: "company:company_currency", + options: "company:company_currency", change: function () { dialog.fields_dict.balance_details.df.data.some(d => { if (d.idx == this.doc.idx) { @@ -134,7 +134,7 @@ erpnext.PointOfSale.Controller = class { set_opening_entry_status() { this.page.set_title_sub( ` - + Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")} `); @@ -199,7 +199,7 @@ erpnext.PointOfSale.Controller = class { if (this.frm.doc.items.length == 0) { frappe.show_alert({ - message:__("You must add atleast one item to save it as draft."), + message:__("You must add atleast one item to save it as draft."), indicator:'red' }); frappe.utils.play_sound("error"); @@ -208,7 +208,7 @@ erpnext.PointOfSale.Controller = class { this.frm.save(undefined, undefined, undefined, () => { frappe.show_alert({ - message:__("There was an error saving the document."), + message:__("There was an error saving the document."), indicator:'red' }); frappe.utils.play_sound("error"); @@ -256,7 +256,7 @@ erpnext.PointOfSale.Controller = class { cart_item_clicked: (item_code, batch_no, uom) => { const item_row = this.frm.doc.items.find( - i => i.item_code === item_code + i => i.item_code === item_code && i.uom === uom && (!batch_no || (batch_no && i.batch_no === batch_no)) ); @@ -429,7 +429,7 @@ erpnext.PointOfSale.Controller = class { }) } - + toggle_recent_order_list(show) { this.toggle_components(!show); @@ -539,7 +539,7 @@ erpnext.PointOfSale.Controller = class { const qty_needed = field === 'qty' ? value * item_row.conversion_factor : item_row.qty * value; await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse); } - + if (this.is_current_item_being_edited(item_row) || item_selected_from_selector) { await frappe.model.set_value(item_row.doctype, item_row.name, field, value); this.update_cart_html(item_row); @@ -577,7 +577,7 @@ erpnext.PointOfSale.Controller = class { this.check_serial_batch_selection_needed(item_row) && this.edit_item_details_of(item_row); this.update_cart_html(item_row); - } + } } catch (error) { console.log(error); } finally { @@ -588,7 +588,7 @@ erpnext.PointOfSale.Controller = class { get_item_from_frm(item_code, batch_no, uom) { const has_batch_no = batch_no; return this.frm.doc.items.find( - i => i.item_code === item_code + i => i.item_code === item_code && (!has_batch_no || (has_batch_no && i.batch_no === batch_no)) && (i.uom === uom) ); @@ -617,7 +617,7 @@ erpnext.PointOfSale.Controller = class { const no_serial_selected = !item_row.serial_no; const no_batch_selected = !item_row.batch_no; - if ((serialized && no_serial_selected) || (batched && no_batch_selected) || + if ((serialized && no_serial_selected) || (batched && no_batch_selected) || (serialized && batched && (no_batch_selected || no_serial_selected))) { return true; } diff --git a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html index 5808ce73ee..7166ba3786 100644 --- a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html +++ b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html @@ -21,7 +21,6 @@

{%= __("Next Steps") %}

diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 9bd03d45cb..faa9b5df2f 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -198,7 +198,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb freeze: true, callback: function(r) { frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); dialog.hide(); callback(r); }, diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 71a3e7abca..7b2edff7e0 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -102,7 +102,7 @@ frappe.ui.form.on('Batch', { }, callback: (r) => { frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); frm.refresh(); }, }); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index faf4accc73..43e18d16fc 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -85,7 +85,7 @@ frappe.ui.form.on("Item", { } if (frm.doc.variant_of) { frm.set_intro(__('This Item is a Variant of {0} (Template).', - [`${frm.doc.variant_of}`]), true); + [`${frm.doc.variant_of}`]), true); } if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) { @@ -649,7 +649,7 @@ $.extend(erpnext.item, { if (r.message) { var variant = r.message; frappe.msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes", - [repl('%(item)s', { + [repl('%(item)s', { item_encoded: encodeURIComponent(variant), item: variant })] diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3b62c38b86..1993d56a6c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -860,7 +860,7 @@ class Item(WebsiteGenerator): rows = '' for docname, attr_list in not_included.items(): - link = "{0}".format(frappe.bold(_(docname))) + link = "{0}".format(frappe.bold(_(docname))) rows += table_row(link, body(attr_list)) error_description = _('The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template.') diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 2729f4b15e..773fddcf96 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -14,6 +14,6 @@ frappe.ui.form.on("Item Price", { frm.add_fetch("item_code", "stock_uom", "uom"); frm.set_df_property("bulk_import_help", "options", - '' + __("Import in Bulk") + ''); + '' + __("Import in Bulk") + ''); } }); diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 940b940aba..e58ac22d0b 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -184,7 +184,7 @@ frappe.ui.form.on("Issue", { let url = window.location.href let arr = url.split("/"); let result = arr[0] + "//" + arr[2] - frappe.msgprint(`New issue created: ${r.message}`) + frappe.msgprint(`New issue created: ${r.message}`) frm.reload_doc(); dialog.hide(); }); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 62b39cced5..e4e7b2543b 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -207,7 +207,7 @@ class Issue(Document): "comment_type": "Info", "reference_doctype": "Issue", "reference_name": replicated_issue.name, - "content": " - Split the Issue from {1}".format(self.name, frappe.bold(self.name)), + "content": " - Split the Issue from {1}".format(self.name, frappe.bold(self.name)), }).insert(ignore_permissions=True) return replicated_issue.name diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py index 0e5e95d1a8..b2e74da921 100644 --- a/erpnext/utilities/bot.py +++ b/erpnext/utilities/bot.py @@ -26,12 +26,12 @@ class FindItemBot(BotParser): for warehouse in warehouses: qty = frappe.db.get_value("Bin", {'item_code': item[0], 'warehouse': warehouse.name}, 'actual_qty') if qty: - out.append(_('{0} units of [{1}](#Form/Item/{1}) found in [{2}](#Form/Warehouse/{2})').format(qty, + out.append(_('{0} units of [{1}](/app/Form/Item/{1}) found in [{2}](/app/Form/Warehouse/{2})').format(qty, item[0], warehouse.name)) found = True if not found: - out.append(_('[{0}](#Form/Item/{0}) is out of stock').format(item[0])) + out.append(_('[{0}](/app/Form/Item/{0}) is out of stock').format(item[0])) return "\n\n".join(out) From 410db04b48291e310b981b541279d0930dcf4eed Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 18 Nov 2020 15:57:16 +0530 Subject: [PATCH 109/283] fix: linter issue for translation syntax --- erpnext/regional/india/utils.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 227af9cdeb..e189da7b11 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -86,7 +86,7 @@ def validate_gstin_check_digit(gstin, label='GSTIN'): factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: frappe.throw(_("""Invalid {0}! The check digit validation has failed. - Please ensure you've typed the {0} correctly.""".format(label))) + Please ensure you've typed the {0} correctly.""").format(label)) def get_itemised_tax_breakup_header(item_doctype, tax_accounts): if frappe.get_meta(item_doctype).has_field('gst_hsn_code'): @@ -160,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return party_details + return if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -168,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges'): + if party_details.get('taxes_and_charges') and return_taxes: return party_details if not party_details.company_gstin: - return party_details + return elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges'): + if party_details.get('taxes_and_charges') and return_taxes: return party_details if not party_details.supplier_gstin: - return party_details + return - if not party_details.place_of_supply: return party_details + if not party_details.place_of_supply: return - if not party_details.company_gstin: return party_details + if not party_details.company_gstin: return if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -197,11 +197,12 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return party_details + return party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - return party_details + if return_taxes: + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): @@ -235,7 +236,7 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code): if tax_category.gst_state == number_state_mapping[state_code] or \ (not default_tax and not tax_category.gst_state): default_tax = frappe.db.get_value(master_doctype, - {'company': company, 'disabled': 0, 'tax_category': tax_category.name}, 'name') + {'disabled': 0, 'tax_category': tax_category.name}, 'name') return default_tax def get_tax_template_for_sez(party_details, master_doctype, company, party_type): From 188657d05a9f0199de898094bbe033e51d52b930 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 18 Nov 2020 17:15:54 +0530 Subject: [PATCH 110/283] fix: email digest user not found --- erpnext/setup/doctype/email_digest/email_digest.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index b30bd7814b..cbb4c7c5de 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -48,12 +48,8 @@ class EmailDigest(Document): recipients = list(filter(lambda r: r in valid_users, self.recipient_list.split("\n"))) - original_user = frappe.session.user - if recipients: for user_id in recipients: - frappe.set_user(user_id) - frappe.set_user_lang(user_id) msg_for_this_recipient = self.get_msg_html() if msg_for_this_recipient: frappe.sendmail( @@ -64,9 +60,6 @@ class EmailDigest(Document): reference_name = self.name, unsubscribe_message = _("Unsubscribe from this Email Digest")) - frappe.set_user(original_user) - frappe.set_user_lang(original_user) - def get_msg_html(self): """Build email digest content""" frappe.flags.ignore_account_permission = True From bbe4933a523b737d4537827685de508cae8dac64 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 18 Nov 2020 20:58:59 +0530 Subject: [PATCH 111/283] fix(regional): set proper state code in ewaybill JSON when gst_category is SEZ --- erpnext/regional/india/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 6164e066cd..5458001e9d 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -516,6 +516,9 @@ def get_address_details(data, doc, company_address, billing_address): data.transType = 1 data.actualToStateCode = data.toStateCode shipping_address = billing_address + + if doc.gst_category == 'SEZ': + data.toStateCode = 99 return data From 6dafe1eabfaad5619257ca7062ea568d44fff2af Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 19 Nov 2020 08:12:58 +0530 Subject: [PATCH 112/283] feat: Formula based Quality Inspection (#23916) * feat: Formula based Quality Inspection * chore: Added Test for Formula Based QI reading --- .../item_quality_inspection_parameter.json | 131 +++++++----------- .../quality_inspection/quality_inspection.py | 31 ++++- .../test_quality_inspection.py | 66 +++++++-- .../quality_inspection_reading.json | 38 ++++- .../quality_inspection_template.py | 6 +- 5 files changed, 176 insertions(+), 96 deletions(-) diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index f1e1fd3679..888bc2de47 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -1,88 +1,57 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:28:01", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:28:01", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "specification", + "value", + "column_break_3", + "acceptance_formula" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "specification", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Parameter", - "length": 0, - "no_copy": 0, - "oldfieldname": "specification", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "specification", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter", + "oldfieldname": "specification", + "oldfieldtype": "Data", + "print_width": "200px", + "reqd": 1, "width": "200px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Acceptance Criteria", - "length": 0, - "no_copy": 0, - "oldfieldname": "value", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Acceptance Criteria", + "oldfieldname": "value", + "oldfieldtype": "Data" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10", + "fieldname": "acceptance_formula", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Acceptance Criteria Formula" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-07-11 03:28:01.074316", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Quality Inspection Parameter", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2020-11-16 16:33:42.421842", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Quality Inspection Parameter", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index c3bb514184..399a63a186 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -4,15 +4,20 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +from frappe import _ +from frappe.utils import flt from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \ import get_template_details -from frappe.model.mapper import get_mapped_doc class QualityInspection(Document): def validate(self): if not self.readings and self.item_code: self.get_item_specification_details() + if self.readings: + self.set_status_based_on_acceptance_formula() + def get_item_specification_details(self): if not self.quality_inspection_template: self.quality_inspection_template = frappe.db.get_value('Item', @@ -26,6 +31,7 @@ class QualityInspection(Document): child = self.append('readings', {}) child.specification = d.specification child.value = d.value + child.acceptance_formula = d.acceptance_formula child.status = "Accepted" def get_quality_inspection_template(self): @@ -58,6 +64,29 @@ class QualityInspection(Document): .format(parent_doc=self.reference_type, child_doc=doctype), (quality_inspection, self.modified, self.reference_name, self.item_code)) + def set_status_based_on_acceptance_formula(self): + for reading in self.readings: + if not reading.acceptance_formula: continue + + condition = reading.acceptance_formula + data = {} + for i in range(1, 11): + field = "reading_" + str(i) + data[field] = flt(reading.get(field)) or 0 + + try: + result = frappe.safe_eval(condition, None, data) + reading.status = "Accepted" if result else "Rejected" + except SyntaxError: + frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx), + title=_("Invalid Formula")) + except NameError as e: + field = frappe.bold(e.args[0].split()[1]) + frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.") + .format(reading.idx, field), + title=_("Invalid Formula")) + + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def item_query(doctype, txt, searchfield, start, page_len, filters): diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index bb535c1f6a..2c40009426 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -7,6 +7,7 @@ import unittest from frappe.utils import nowdate from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note +from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError # test_records = frappe.get_test_records('Quality Inspection') @@ -17,10 +18,12 @@ class TestQualityInspection(unittest.TestCase): frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1) def test_qa_for_delivery(self): + make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100) dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + self.assertRaises(QualityInspectionRequiredError, dn.submit) - qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected", submit=True) + qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected") dn.reload() self.assertRaises(QualityInspectionRejectedError, dn.submit) @@ -28,12 +31,51 @@ class TestQualityInspection(unittest.TestCase): dn.reload() dn.submit() + qa.cancel() + dn.reload() + dn.cancel() + def test_qa_not_submit(self): dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) - qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, submit = False) + qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True) dn.items[0].quality_inspection = qa.name self.assertRaises(QualityInspectionNotSubmittedError, dn.submit) + qa.delete() + dn.delete() + + def test_formula_based_qi_readings(self): + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + readings = [{ + "specification": "Iron Content", + "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50", + "reading_1": 0.4 + }, + { + "specification": "Calcium Content", + "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50", + "reading_1": 0.7 + }, + { + "specification": "Mg Content", + "acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9", + "reading_1": 0.5, + "reading_2": 0.7, + "reading_3": "random text" # check if random string input causes issues + }] + + qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, + readings=readings, do_not_save=True) + qa.save() + + # status must be auto set as per formula + self.assertEqual(qa.readings[0].status, "Accepted") + self.assertEqual(qa.readings[1].status, "Rejected") + self.assertEqual(qa.readings[2].status, "Accepted") + + qa.delete() + dn.delete() + def create_quality_inspection(**args): args = frappe._dict(args) qa = frappe.new_doc("Quality Inspection") @@ -44,12 +86,18 @@ def create_quality_inspection(**args): qa.item_code = args.item_code or "_Test Item with QA" qa.sample_size = 1 qa.inspected_by = frappe.session.user - qa.append("readings", { - "specification": "Size", - "status": args.status - }) - qa.save() - if args.submit: - qa.submit() + + readings = args.readings or {"specification": "Size", "status": args.status} + + if isinstance(readings, list): + for entry in readings: + qa.append("readings", entry) + else: + qa.append("readings", readings) + + if not args.do_not_save: + qa.save() + if not args.do_not_submit: + qa.submit() return qa diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index f9f8a71c02..c1976dd1fb 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -1,22 +1,29 @@ { + "actions": [], "autoname": "hash", "creation": "2013-02-22 01:27:43", "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "specification", "value", + "status", + "column_break_4", + "acceptance_formula", + "section_break_3", "reading_1", "reading_2", "reading_3", + "column_break_10", "reading_4", "reading_5", "reading_6", + "column_break_14", "reading_7", "reading_8", "reading_9", - "reading_10", - "status" + "reading_10" ], "fields": [ { @@ -124,15 +131,40 @@ "oldfieldname": "status", "oldfieldtype": "Select", "options": "Accepted\nRejected" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10", + "fieldname": "acceptance_formula", + "fieldtype": "Code", + "label": "Acceptance Criteria Formula" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" } ], "idx": 1, "istable": 1, - "modified": "2019-07-11 18:48:12.667404", + "links": [], + "modified": "2020-11-16 16:34:29.947856", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", "owner": "Administrator", "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py index 0d9a90312b..e2848469b8 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -12,5 +12,7 @@ class QualityInspectionTemplate(Document): def get_template_details(template): if not template: return [] - return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value"], - filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file + return frappe.get_all('Item Quality Inspection Parameter', + fields=["specification", "value", "acceptance_formula"], + filters={'parenttype': 'Quality Inspection Template', 'parent': template}, + order_by="idx") \ No newline at end of file From 14514f776b7134282c98d40392d89a714ddee68e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 19 Nov 2020 08:17:16 +0530 Subject: [PATCH 113/283] fix: Enable Allow Auto Repeat for standard doctypes having auto_repeat field (#23776) Co-authored-by: Shivam Mishra --- erpnext/accounts/doctype/journal_entry/journal_entry.json | 3 ++- erpnext/accounts/doctype/payment_entry/payment_entry.json | 3 ++- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 3 ++- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 6 ++++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 3 ++- erpnext/buying/doctype/purchase_order/purchase_order.json | 3 ++- .../doctype/supplier_quotation/supplier_quotation.json | 3 ++- erpnext/selling/doctype/quotation/quotation.json | 3 ++- erpnext/selling/doctype/sales_order/sales_order.json | 3 ++- erpnext/stock/doctype/delivery_note/delivery_note.json | 1 + .../stock/doctype/purchase_receipt/purchase_receipt.json | 3 ++- 11 files changed, 23 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 4573c50134..b7bbb74ce9 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-03-25 10:53:52", @@ -503,7 +504,7 @@ "idx": 176, "is_submittable": 1, "links": [], - "modified": "2020-06-02 18:15:46.955697", + "modified": "2020-10-30 13:56:01.121995", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 72149a665d..2e1f201e25 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2016-06-01 14:38:51.012597", @@ -587,7 +588,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-09-02 13:39:43.383705", + "modified": "2020-10-30 13:56:20.007336", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 1cff3c661d..5bc57b4a84 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2020-01-24 15:29:29.933693", @@ -1580,7 +1581,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2020-09-28 16:51:24.641755", + "modified": "2020-10-30 13:56:51.056083", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 8925b87b52..2df77a84c7 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -1334,7 +1335,8 @@ "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, - "modified": "2020-09-21 12:22:09.164068", + "links": [], + "modified": "2020-10-30 13:57:18.266978", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1396,4 +1398,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index ae40153890..17fbe2def9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -1955,7 +1956,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-10-09 15:59:57.544736", + "modified": "2020-10-30 13:57:45.086303", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 4b865a98e9..71231f68c8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -1105,7 +1106,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-10-30 11:39:37.388249", + "modified": "2020-10-30 13:58:14.697921", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 9a092ca5c3..b39c989073 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:45", @@ -807,7 +808,7 @@ "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-10-01 20:56:17.932007", + "modified": "2020-10-30 13:58:33.043971", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 5b85187ccb..3eba62bc19 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:08", @@ -932,7 +933,7 @@ "is_submittable": 1, "links": [], "max_attachments": 1, - "modified": "2020-07-26 17:46:19.951223", + "modified": "2020-10-30 13:58:59.212060", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 77c1787c26..3d64ac3780 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-06-18 12:39:59", @@ -1460,7 +1461,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-10-07 14:30:01.782617", + "modified": "2020-10-30 13:59:18.628077", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 3c5129b1ab..7393c8a70e 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:09", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index ce54fc883f..13c8ceb759 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -1109,7 +1110,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-08-03 23:20:26.381024", + "modified": "2020-10-30 14:00:08.347534", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From cd05b34691d7ef50d06791820afddb184259e1b3 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 19 Nov 2020 11:37:08 +0530 Subject: [PATCH 114/283] fix: company filter added again --- erpnext/regional/india/utils.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e189da7b11..c6620aa92b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -160,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return + return party_details if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -168,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.company_gstin: - return + return party_details elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.supplier_gstin: - return + return party_details - if not party_details.place_of_supply: return + if not party_details.place_of_supply: return party_details - if not party_details.company_gstin: return + if not party_details.company_gstin: return party_details if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -197,12 +197,11 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return + return party_details party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - if return_taxes: - return party_details + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): @@ -236,7 +235,7 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code): if tax_category.gst_state == number_state_mapping[state_code] or \ (not default_tax and not tax_category.gst_state): default_tax = frappe.db.get_value(master_doctype, - {'disabled': 0, 'tax_category': tax_category.name}, 'name') + {'company': company, 'disabled': 0, 'tax_category': tax_category.name}, 'name') return default_tax def get_tax_template_for_sez(party_details, master_doctype, company, party_type): From c1187bc1d598d6d771f2e0379b654a63adddb84b Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 19 Nov 2020 13:10:24 +0530 Subject: [PATCH 115/283] fix: duplicate items validation for POS Invoice when allow multiple items is disabled (#23896) * fix: duplicate items validation for POS when allow multiple items in disabled * fix: variable name change for duplicate item validation Co-authored-by: pateljannat --- erpnext/controllers/selling_controller.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 7504746e07..515239a982 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -416,26 +416,26 @@ class SellingController(StockController): return for d in self.get('items'): - if self.doctype == "Sales Invoice": - e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or ''] - f = [d.item_code, d.description, d.sales_order or d.delivery_note] + if self.doctype in ["POS Invoice","Sales Invoice"]: + stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or ''] + non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note] elif self.doctype == "Delivery Note": - e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] - f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] + stock_items = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] + non_stock_items = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] elif self.doctype in ["Sales Order", "Quotation"]: - e = [d.item_code, d.description, d.warehouse, ''] - f = [d.item_code, d.description] + stock_items = [d.item_code, d.description, d.warehouse, ''] + non_stock_items = [d.item_code, d.description] if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: - if e in check_list: + if stock_items in check_list: frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) else: - check_list.append(e) + check_list.append(stock_items) else: - if f in chk_dupl_itm: + if non_stock_items in chk_dupl_itm: frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) else: - chk_dupl_itm.append(f) + chk_dupl_itm.append(non_stock_items) def validate_target_warehouse(self): items = self.get("items") + (self.get("packed_items") or []) From f0b1670abc331bf7aad8eb7d746482648b710e65 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Nov 2020 18:40:13 +0530 Subject: [PATCH 116/283] fix: tds test case --- .../test_tax_withholding_category.py | 12 ++++++++---- .../buying/doctype/supplier/test_supplier.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index a0b0cbb995..ef77674372 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -7,6 +7,7 @@ import frappe import unittest from frappe.utils import today from erpnext.accounts.utils import get_fiscal_year +from erpnext.buying.doctype.supplier.test_supplier import create_supplier test_dependencies = ["Supplier Group"] @@ -103,17 +104,20 @@ class TestTaxWithholdingCategory(unittest.TestCase): def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self): invoices = [] - frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS") - pi = create_purchase_invoice(supplier="Test TDS Supplier2") + doc = create_supplier(supplier_name = "Test TDS Supplier ABC", + tax_withholding_category="Single Threshold TDS") + supplier = doc.name + + pi = create_purchase_invoice(supplier=supplier) pi.submit() invoices.append(pi) # TDS not applied - pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True) + pi = create_purchase_invoice(supplier=supplier, do_not_apply_tds=True) pi.submit() invoices.append(pi) - pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi = create_purchase_invoice(supplier=supplier) pi.submit() invoices.append(pi) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index a377ec90f8..f9c8d35518 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -120,3 +120,20 @@ class TestSupplier(unittest.TestCase): # Rollback address.delete() + +def create_supplier(**args): + args = frappe._dict(args) + + try: + doc = frappe.get_doc({ + "doctype": "Supplier", + "supplier_name": args.supplier_name, + "supplier_group": args.supplier_group or "Services", + "supplier_type": args.supplier_type or "Company", + "tax_withholding_category": args.tax_withholding_category + }).insert() + + return doc + + except frappe.DuplicateEntryError: + return frappe.get_doc("Supplier", args.supplier_name) \ No newline at end of file From 1d5d863e9a7e71c540e9ba032ef042e218667a56 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 19 Nov 2020 20:11:45 +0530 Subject: [PATCH 117/283] fix: removing return_taxes condition --- erpnext/regional/india/taxes.js | 3 +-- erpnext/regional/india/utils.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index ecfa9b7cdf..3c156479c5 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -31,8 +31,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => { args: { party_details: JSON.stringify(party_details), doctype: frm.doc.doctype, - company: frm.doc.company, - return_taxes: 1 + company: frm.doc.company }, callback: function(r) { if(r.message) { diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index c6620aa92b..8d89335717 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -150,7 +150,7 @@ def get_place_of_supply(party_details, doctype): return cstr(address.gst_state_number) + "-" + cstr(address.gst_state) @frappe.whitelist() -def get_regional_address_details(party_details, doctype, company, return_taxes=None): +def get_regional_address_details(party_details, doctype, company): if isinstance(party_details, string_types): party_details = json.loads(party_details) party_details = frappe._dict(party_details) From ceab692f7313acfb11704dd50a72738a9c3be9c0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Nov 2020 17:57:35 +0530 Subject: [PATCH 118/283] fix: incorrect delink serial no and batch --- erpnext/controllers/stock_controller.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f743d707f7..2d2fff8fd5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -229,9 +229,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ - Account in the Items table").format(item.idx, frappe.bold(item.item_code)), - title=_("Expense Account Missing")) + msg = _("Please set an Expense Account in the Items table") + frappe.throw(_("Row #{0}: Expense Account not set for the Item {1}. {2}") + .format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", @@ -247,7 +247,9 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue - serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + serial_nos = [sr.name for sr in frappe.get_all("Serial No", + {'batch_no': d.batch_no, 'status': 'Inactive'})] + if serial_nos: frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) From 34d07b630669a6d18e3289df9561a50dcc3371fa Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Fri, 20 Nov 2020 19:22:35 +0530 Subject: [PATCH 119/283] fix: purchase receipt to purchase invoice bill date mapping (#23967) Co-authored-by: pateljannat --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d964669830..2cc4679c8c 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -572,7 +572,8 @@ def make_purchase_invoice(source_name, target_doc=None): "doctype": "Purchase Invoice", "field_map": { "supplier_warehouse":"supplier_warehouse", - "is_return": "is_return" + "is_return": "is_return", + "bill_date": "bill_date" }, "validation": { "docstatus": ["=", 1], From 20d6143c382a677db5464a0b383962e746c4b3a5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 21 Nov 2020 12:09:11 +0530 Subject: [PATCH 120/283] fix: Validation for journal entry with 0 debit and credit values --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d8394785c6..0b3205523f 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -34,6 +34,7 @@ class JournalEntry(AccountsController): self.validate_entries_for_advance() self.validate_multi_currency() self.set_amounts_in_company_currency() + self.validate_debit_credit_amount() self.validate_total_debit_and_credit() self.validate_against_jv() self.validate_reference_doc() @@ -369,6 +370,11 @@ class JournalEntry(AccountsController): if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited))) if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited))) + def validate_debit_credit_amount(self): + for d in self.get('accounts'): + if not flt(d.debit) and not flt(d.credit): + frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx)) + def validate_total_debit_and_credit(self): self.set_total_debit_credit() if self.difference: From 610d9ca64937366da56ad2ef7610def4c5d36b57 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Nov 2020 13:47:49 +0530 Subject: [PATCH 121/283] fix: bom stock report color issue --- .../report/bom_stock_report/bom_stock_report.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 2ac6fa073b..45331c6af8 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -25,11 +25,11 @@ frappe.query_reports["BOM Stock Report"] = { ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.id == "Item"){ + if (column.id == "item"){ if (data["Enough Parts to Build"] > 0){ - value = `${data['Item']}` + value = `${data['item']}`; } else { - value = `${data['Item']}` + value = `${data['item']}`; } } return value From 34f381df172f7c40db4f51317a22d8f29868b0d3 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 23 Nov 2020 15:31:08 +0530 Subject: [PATCH 122/283] fix: enabling track changes for stock settings --- erpnext/stock/doctype/stock_settings/stock_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 067659f64a..a1666579d1 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -217,7 +217,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-10-13 10:33:29.147682", + "modified": "2020-11-23 15:26:54.225608", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", @@ -235,5 +235,6 @@ ], "quick_entry": 1, "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file From f9a44000d9b06517dc2fd10916eac3f8d0c02b8e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 23 Nov 2020 15:40:06 +0530 Subject: [PATCH 123/283] Update bom_stock_report.js --- .../manufacturing/report/bom_stock_report/bom_stock_report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 45331c6af8..84f5c346ca 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -25,8 +25,8 @@ frappe.query_reports["BOM Stock Report"] = { ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.id == "item"){ - if (data["Enough Parts to Build"] > 0){ + if (column.id == "item") { + if (data["Enough Parts to Build"] > 0) { value = `${data['item']}`; } else { value = `${data['item']}`; From 0fb2e02b5626e9dfd4fcebf6511c62b6d05f1352 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 9 Nov 2020 23:51:24 +0530 Subject: [PATCH 124/283] feat: pos item selector new ui --- erpnext/public/build.json | 3 +- erpnext/public/scss/point-of-sale.scss | 145 ++++++++++++++++++ .../page/point_of_sale/pos_controller.js | 4 +- .../page/point_of_sale/pos_item_selector.js | 42 +++-- 4 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 erpnext/public/scss/point-of-sale.scss diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 2695502269..78128a105d 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -2,7 +2,8 @@ "css/erpnext.css": [ "public/less/erpnext.less", "public/less/hub.less", - "public/less/call_popup.less" + "public/less/call_popup.less", + "public/scss/point-of-sale.scss" ], "css/marketplace.css": [ "public/less/hub.less" diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss new file mode 100644 index 0000000000..1d01496277 --- /dev/null +++ b/erpnext/public/scss/point-of-sale.scss @@ -0,0 +1,145 @@ +.point-of-sale-app { + display: grid; + grid-template-columns: repeat(10, minmax(0, 1fr)); + gap: var(--margin-md); + + section { + min-height: 45rem; + max-height: calc(100vh - 200px); + } + + .frappe-control { + margin: 0 !important; + padding: 5px 5px; + width: 100%; + } + + .form-group { + margin-bottom: 0px !important; + } + + .pointer-no-select { + cursor: pointer; + user-select: none; + } + + .nowrap { + overflow: hidden; + white-space: nowrap; + } + + .image { + height: 100% !important; + object-fit: cover; + } + + .abbr { + background-color: var(--gray-50); + font-size: var(--text-3xl); + } + + .label { + display: flex; + align-items: center; + font-weight: 700; + font-size: var(--text-lg); + } + + .pos-card { + background-color: var(--fg-color); + box-shadow: var(--shadow-base); + border-radius: var(--border-radius-md); + } + + > .items-selector { + grid-column: span 6 / span 6; + display: flex; + flex-direction: column; + overflow-y: scroll; + overflow-x: hidden; + + > .filter-section { + display: grid; + grid-template-columns: repeat(12, minmax(0, 1fr)); + background-color: var(--fg-color); + position: sticky; + top: -1px; + z-index: 1; + padding: var(--padding-md); + padding-bottom: var(--padding-sm); + align-items: center; + + > .label { + @extend .label; + grid-column: span 4 / span 4; + padding: var(--padding-xs); + padding-top: 0px; + } + + > .search-field { + grid-column: span 5 / span 5; + display: flex; + align-items: center; + } + + > .item-group-field { + grid-column: span 3 / span 3; + display: flex; + align-items: center; + } + } + + > .items-container { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--margin-lg); + flex: 1 1 0%; + padding: var(--padding-lg); + padding-top: var(--padding-xs); + + > .item-wrapper { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-base); + + .item-display { + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius-md); + margin: var(--margin-sm); + margin-bottom: 0px; + min-height: 8rem; + height: 8rem; + color: var(--gray-500); + + > img { + @extend .image; + } + } + + > .item-detail { + display: flex; + flex-direction: column; + justify-content: center; + min-height: 3.5rem; + height: 3.5rem; + padding-left: var(--padding-sm); + padding-right: var(--padding-sm); + + > .item-name { + @extend .nowrap; + display: flex; + align-items: center; + font-size: var(--text-md); + } + + > .item-rate { + font-weight: 700; + } + } + + } + } + } +} \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 288084d245..8cbd59086c 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -157,10 +157,10 @@ erpnext.PointOfSale.Controller = class { prepare_dom() { this.wrapper.append( - `
` + `
` ); - this.$components_wrapper = this.wrapper.find('.app'); + this.$components_wrapper = this.wrapper.find('.point-of-sale-app'); } prepare_components() { diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 49d42814ab..4195d93d4e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -17,18 +17,13 @@ erpnext.PointOfSale.ItemSelector = class { prepare_dom() { this.wrapper.append( - `
-
-
-
-
-
-
-
ALL ITEMS
-
-
-
+ `
+
+
All Items
+
+
+
` ); @@ -80,27 +75,28 @@ erpnext.PointOfSale.ItemSelector = class { function get_item_image_html() { if (item_image) { - return `
- ${frappe.get_abbr(item.item_name)} -
` + return `
+ ${frappe.get_abbr(item.item_name)} +
`; } else { - return `
- ${frappe.get_abbr(item.item_name)} -
` + return `
${frappe.get_abbr(item.item_name)}
`; } } return ( - `
+ ${get_item_image_html()} -
-
+ +
+
${frappe.ellipsis(item.item_name, 18)}
-
${format_currency(item.price_list_rate, item.currency, 0) || 0}
+
${format_currency(item.price_list_rate, item.currency, 0) || 0}
` ) @@ -115,7 +111,7 @@ erpnext.PointOfSale.ItemSelector = class { df: { label: __('Search'), fieldtype: 'Data', - placeholder: __('Search by item code, serial number, batch no or barcode') + placeholder: __('Search by item code, serial number or barcode') }, parent: this.$component.find('.search-field'), render_input: true, From 210baafad4e0fa12fc70999d5830998ba3e7c26c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 12:04:00 +0530 Subject: [PATCH 125/283] feat: pos customer selector new ui --- erpnext/public/scss/point-of-sale.scss | 169 ++++++++++++++ .../page/point_of_sale/pos_item_cart.js | 217 ++++++++++-------- 2 files changed, 285 insertions(+), 101 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 1d01496277..4d5bc21de5 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -51,6 +51,10 @@ border-radius: var(--border-radius-md); } + .seperator { + border-bottom: 1px solid var(--gray-300); + } + > .items-selector { grid-column: span 6 / span 6; display: flex; @@ -142,4 +146,169 @@ } } } + + > .customer-cart-container { + grid-column: span 4 / span 4; + display: flex; + flex-direction: column; + + > .customer-section { + @extend .pos-card; + display: flex; + flex-direction: column; + padding: var(--padding-md); + + > .customer-field { + display: flex; + align-items: center; + } + + > .customer-details { + display: flex; + flex-direction: column; + position: sticky; + top: -1px; + z-index: 1; + background-color: var(--fg-color); + + > .header { + display: flex; + margin-bottom: var(--margin-md); + justify-content: space-between; + padding-top: var(--padding-md); + + > .label { + @extend .label; + } + + > .close-details-btn { + display: flex; + align-items: center; + cursor: pointer; + } + } + + > .customer-display { + display: flex; + align-items: center; + cursor: pointer; + + > .customer-image { + display: flex; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + border-radius: 50%; + color: var(--gray-500); + margin-right: var(--margin-md); + + > img { + @extend .image; + border-radius: 50%; + } + } + + > .customer-abbr { + @extend .abbr; + font-size: var(--text-2xl); + } + + > .customer-name-desc { + @extend .nowrap; + display: flex; + flex-direction: column; + margin-right: auto; + + >.customer-name { + font-weight: 700; + font-size: var(--text-lg); + } + + >.customer-desc { + color: var(--gray-600); + font-weight: 500; + font-size: var(--text-sm); + } + } + + > .reset-customer-btn { + display: flex; + align-items: center; + cursor: pointer; + } + + } + + > .customer-fields-container { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: var(--margin-sm); + } + + > .transactions-label { + @extend .label; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-sm); + } + } + } + + > .cart-container { + + } + } + + .invoice-wrapper { + @extend .pointer-no-select; + display: flex; + justify-content: space-between; + border-radius: var(--border-radius-md); + padding: var(--padding-sm); + + &:hover { + background-color: var(--gray-50); + } + + > .invoice-name-date { + display: flex; + flex-direction: column; + justify-content: end; + + > .invoice-name { + @extend .nowrap; + font-size: var(--text-md); + font-weight: 700; + margin-bottom: var(--margin-xs); + } + + > .invoice-date { + @extend .nowrap; + font-size: var(--text-sm); + display: flex; + align-items: center; + } + } + + > .invoice-total-status { + display: flex; + flex-direction: column; + font-weight: 500; + font-size: var(--text-sm); + margin-left: var(--margin-md); + + > .invoice-total { + margin-bottom: var(--margin-xs); + font-size: var(--text-base); + font-weight: 700; + text-align: right; + } + + > .invoice-status { + display: flex; + align-items: center; + justify-content: right; + } + } + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 7799dacacb..11453f7cf0 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -16,10 +16,10 @@ erpnext.PointOfSale.ItemCart = class { prepare_dom() { this.wrapper.append( - `
` + `
` ) - this.$component = this.wrapper.find('.item-cart'); + this.$component = this.wrapper.find('.customer-cart-container'); } init_child_components() { @@ -29,7 +29,7 @@ erpnext.PointOfSale.ItemCart = class { init_customer_selector() { this.$component.append( - `
` + `
` ) this.$customer_section = this.$component.find('.customer-section'); } @@ -37,21 +37,20 @@ erpnext.PointOfSale.ItemCart = class { reset_customer_selector() { const frm = this.events.get_frm(); frm.set_value('customer', ''); - this.$customer_section.removeClass('border pr-4 pl-4'); this.make_customer_selector(); this.customer_field.set_focus(); } init_cart_components() { this.$component.append( - `
+ `
Item
Qty
Amount
-
+
@@ -88,7 +87,7 @@ erpnext.PointOfSale.ItemCart = class { `
+ Add Discount
-
+
Net Total
@@ -106,7 +105,7 @@ erpnext.PointOfSale.ItemCart = class {
0.00
-
+
Checkout
@@ -151,7 +150,7 @@ erpnext.PointOfSale.ItemCart = class { this.$numpad_section.append( `
+ text-center text-white no-select pointer rounded-md text-md text-bold mt-4" data-button-value="checkout"> Checkout
` ) @@ -159,15 +158,17 @@ erpnext.PointOfSale.ItemCart = class { bind_events() { const me = this; - this.$customer_section.on('click', '.add-remove-customer', function (e) { - const customer_info_is_visible = me.$cart_container.hasClass('d-none'); - customer_info_is_visible ? - me.toggle_customer_info(false) : me.reset_customer_selector(); + this.$customer_section.on('click', '.reset-customer-btn', function (e) { + me.reset_customer_selector(); }); - this.$customer_section.on('click', '.customer-header', function(e) { - // don't triggger the event if .add-remove-customer btn is clicked which is under .customer-header - if ($(e.target).closest('.add-remove-customer').length) return; + this.$customer_section.on('click', '.close-details-btn', function (e) { + me.toggle_customer_info(false); + }); + + this.$customer_section.on('click', '.customer-display', function(e) { + // don't triggger the event if .reset-customer-btn btn is clicked which is under .customer-header + if ($(e.target).closest('.reset-customer-btn').length) return; const show = !me.$cart_container.hasClass('d-none'); me.toggle_customer_info(show); @@ -282,24 +283,26 @@ erpnext.PointOfSale.ItemCart = class { toggle_item_highlight(item) { const $cart_item = $(item); - const item_is_highlighted = $cart_item.hasClass("shadow"); + const item_is_highlighted = $cart_item.hasClass("shadow-base"); if (!item || item_is_highlighted) { this.item_is_selected = false; - this.$cart_container.find('.cart-item-wrapper').removeClass("shadow").css("opacity", "1"); + this.$cart_container.find('.cart-item-wrapper').removeClass("shadow-base").css("opacity", "1"); } else { - $cart_item.addClass("shadow"); + $cart_item.addClass("shadow-base"); this.item_is_selected = true; this.$cart_container.find('.cart-item-wrapper').css("opacity", "1"); - this.$cart_container.find('.cart-item-wrapper').not(item).removeClass("shadow").css("opacity", "0.65"); + this.$cart_container.find('.cart-item-wrapper').not(item).removeClass("shadow-base").css("opacity", "0.65"); } - // highlight with inner shadow - // $cart_item.addClass("shadow-inner bg-selected"); - // me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-inner bg-selected"); + // highlight with inner shadow-base + // $cart_item.addClass("shadow-base-inner bg-selected"); + // me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-base-inner bg-selected"); } make_customer_selector() { - this.$customer_section.html(`
`); + this.$customer_section.html(` +
+ `); const me = this; const query = { query: 'erpnext.controllers.queries.customer_query' }; const allowed_customer_group = this.events.get_allowed_customer_group() || []; @@ -313,6 +316,7 @@ erpnext.PointOfSale.ItemCart = class { label: __('Customer'), fieldtype: 'Link', options: 'Customer', + input_class: 'input-xs', placeholder: __('Search by customer name, phone, email.'), get_query: () => query, onchange: function() { @@ -332,7 +336,7 @@ erpnext.PointOfSale.ItemCart = class { } }, }, - parent: this.$customer_section.find('.customer-search-field'), + parent: this.$customer_section.find('.customer-field'), render_input: true, }); this.customer_field.toggle_label(false); @@ -414,7 +418,7 @@ erpnext.PointOfSale.ItemCart = class { stroke-linecap="round" stroke-linejoin="round"> -
+
${String(discount).bold()}% off
` @@ -423,18 +427,18 @@ erpnext.PointOfSale.ItemCart = class { } update_customer_section() { - const { customer, email_id='', mobile_no='', image } = this.customer_info || {}; + const { customer, email_id='', mobile_no='' } = this.customer_info || {}; if (customer) { - this.$customer_section.addClass('border pr-4 pl-4').html( - `
-
- ${get_customer_image()} -
-
${customer}
+ this.$customer_section.html( + `
+
+ ${this.get_customer_image()} +
+
${customer}
${get_customer_description()}
-
+
@@ -449,26 +453,23 @@ erpnext.PointOfSale.ItemCart = class { function get_customer_description() { if (!email_id && !mobile_no) { - return `
Click to add email / phone
` + return `
Click to add email / phone
` } else if (email_id && !mobile_no) { - return `
${email_id}
` + return `
${email_id}
` } else if (mobile_no && !email_id) { - return `
${mobile_no}
` + return `
${mobile_no}
` } else { - return `
${email_id} | ${mobile_no}
` + return `
${email_id} - ${mobile_no}
` } } - function get_customer_image() { - if (image) { - return `
- ${image} -
` - } else { - return `
- ${frappe.get_abbr(customer)} -
` - } + } + get_customer_image() { + const { customer, image } = this.customer_info || {}; + if (image) { + return `
${image}
` + } else { + return `
${frappe.get_abbr(customer)}
` } } @@ -523,7 +524,7 @@ erpnext.PointOfSale.ItemCart = class { let margin_left = ''; if (i !== 0) margin_left = 'ml-2'; const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `${description}` + return `${description}` }).join('') }
@@ -575,7 +576,7 @@ erpnext.PointOfSale.ItemCart = class { if (!$item_to_update.length) { this.$cart_items_wrapper.append( - `
` @@ -618,7 +619,7 @@ erpnext.PointOfSale.ItemCart = class { if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) { return `
-
+
${item_data.qty || 0}
@@ -629,7 +630,7 @@ erpnext.PointOfSale.ItemCart = class { } else { return `
-
+
${item_data.qty || 0}
@@ -753,25 +754,25 @@ erpnext.PointOfSale.ItemCart = class { } highlight_numpad_btn($btn, curr_action) { - const curr_action_is_highlighted = $btn.hasClass('shadow-inner'); + const curr_action_is_highlighted = $btn.hasClass('shadow-base-inner'); const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action); if (!curr_action_is_highlighted) { - $btn.addClass('shadow-inner bg-selected'); + $btn.addClass('shadow-base-inner bg-selected'); } if (this.prev_action === curr_action && curr_action_is_highlighted) { // if Qty is pressed twice - $btn.removeClass('shadow-inner bg-selected'); + $btn.removeClass('shadow-base-inner bg-selected'); } if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) { // Order: Qty -> Rate then remove Qty highlight const prev_btn = $(`[data-button-value='${this.prev_action}']`); - prev_btn.removeClass('shadow-inner bg-selected'); + prev_btn.removeClass('shadow-base-inner bg-selected'); } if (!curr_action_is_action || curr_action === 'done') { // if numbers are clicked setTimeout(() => { - $btn.removeClass('shadow-inner bg-selected'); + $btn.removeClass('shadow-base-inner bg-selected'); }, 100); } } @@ -790,7 +791,7 @@ erpnext.PointOfSale.ItemCart = class { reset_numpad() { this.numpad_value = ''; this.prev_action = undefined; - this.$numpad_section.find('.shadow-inner').removeClass('shadow-inner bg-selected'); + this.$numpad_section.find('.shadow-base-inner').removeClass('shadow-base-inner bg-selected'); } toggle_numpad_field_edit(fieldname) { @@ -801,48 +802,60 @@ erpnext.PointOfSale.ItemCart = class { toggle_customer_info(show) { if (show) { - this.$cart_container.addClass('d-none') - this.$customer_section.addClass('flex-1 scroll-y').removeClass('mb-0 border pr-4 pl-4') - this.$customer_section.find('.icon').addClass('w-24 h-24 text-2xl').removeClass('w-12 h-12 text-md') - this.$customer_section.find('.customer-header').removeClass('h-18'); - this.$customer_section.find('.customer-details').addClass('sticky z-100 bg-white'); + const { customer } = this.customer_info || {}; - this.$customer_section.find('.customer-name').html( - `
${this.customer_info.customer}
-
` - ) - - this.$customer_section.find('.customer-details').append( - `
-
CONTACT DETAILS
-
- -
-
-
+ this.$cart_container.addClass('d-none'); + this.$customer_section.css({ + 'height': '100%', + 'padding-top': '0px', + 'overflow-x': 'hidden', + 'overflow-y': 'scroll' + }); + this.$customer_section.find('.customer-details').html( + `
+
Contact Details
+
+ + +
-
RECENT TRANSACTIONS
-
` - ) +
+
+ ${this.get_customer_image()} +
+
${customer}
+
+
+
+
+ +
+
+
+
+
Recent Transactions
` + ); // transactions need to be in diff div from sticky elem for scrolling - this.$customer_section.append(`
`) + this.$customer_section.append(`
`) - this.render_customer_info_form(); + this.render_customer_fields(); this.fetch_customer_transactions(); } else { this.$cart_container.removeClass('d-none'); - this.$customer_section.removeClass('flex-1 scroll-y').addClass('mb-0 border pr-4 pl-4'); - this.$customer_section.find('.icon').addClass('w-12 h-12 text-md').removeClass('w-24 h-24 text-2xl'); - this.$customer_section.find('.customer-header').addClass('h-18') - this.$customer_section.find('.customer-details').removeClass('sticky z-100 bg-white'); + this.$customer_section.css({ + 'height': '', + 'padding-top': '', + 'overflow-x': '', + 'overflow-y': '' + }); this.update_customer_section(); } } - render_customer_info_form() { - const $customer_form = this.$customer_section.find('.customer-form'); + render_customer_fields() { + const $customer_form = this.$customer_section.find('.customer-fields-container'); const dfs = [{ fieldname: 'email_id', @@ -864,7 +877,7 @@ erpnext.PointOfSale.ItemCart = class { },{ fieldname: 'loyalty_points', label: __('Loyalty Points'), - fieldtype: 'Int', + fieldtype: 'Data', read_only: 1 }]; @@ -916,14 +929,14 @@ erpnext.PointOfSale.ItemCart = class { const transaction_container = this.$customer_section.find('.customer-transactions'); if (!res.length) { - transaction_container.removeClass('flex-1 border rounded').html( - `
No recent transactions found
` + transaction_container.html( + `
No recent transactions found
` ) return; }; const elapsed_time = moment(res[0].posting_date+" "+res[0].posting_time).fromNow(); - this.$customer_section.find('.last-transacted-on').html(`Last transacted ${elapsed_time}`); + this.$customer_section.find('.customer-desc').html(`Last transacted ${elapsed_time}`); res.forEach(invoice => { const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma"); @@ -934,20 +947,22 @@ erpnext.PointOfSale.ItemCart = class { if (invoice.status === 'Return') (indicator_color = 'grey'); transaction_container.append( - `
-
-
${invoice.name}
-
- ${posting_datetime} -
+ `
+
+
${invoice.name}
+
${posting_datetime}
-
-
+
+
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
-
${invoice.status}
+
+ + ${invoice.status} +
-
` +
+
` ) }); }) From cc208839cfd4b23fa01a1c76d90675cf2a3e6e94 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 20:47:20 +0530 Subject: [PATCH 126/283] feat: pos cart with new ui --- erpnext/public/scss/point-of-sale.scss | 269 +++++++++++++++++ .../page/point_of_sale/pos_item_cart.js | 276 ++++++++---------- .../page/point_of_sale/pos_number_pad.js | 5 +- 3 files changed, 396 insertions(+), 154 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 4d5bc21de5..37ca8fd13d 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -52,9 +52,31 @@ } .seperator { + margin-left: var(--margin-sm); + margin-right: var(--margin-sm); border-bottom: 1px solid var(--gray-300); } + .primary-action { + @extend .pointer-no-select; + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-sm); + margin: var(--margin-xs); + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + border-radius: var(--border-radius-md); + font-size: var(--text-lg); + font-weight: 700; + } + + .highlighted-numpad-btn { + box-shadow: inset 0 0px 4px 0px rgba(0, 0, 0, 0.15) !important; + font-weight: 700; + background-color: var(--gray-50); + } + > .items-selector { grid-column: span 6 / span 6; display: flex; @@ -255,7 +277,254 @@ } > .cart-container { + @extend .pos-card; + display: flex; + flex-direction: column; + align-items: center; + margin-top: var(--margin-md); + position: relative; + height: 100%; + > .abs-cart-container { + position: absolute; + display: flex; + flex-direction: column; + padding: var(--padding-md); + width: 100%; + height: 100%; + + > .cart-label { + @extend .label; + padding-bottom: var(--padding-md); + padding-left: var(--margin-sm); + } + + > .cart-header { + display: flex; + width: 100%; + font-size: var(--text-md); + padding-left: var(--padding-xs); + padding-right: var(--padding-xs); + padding-bottom: var(--padding-md); + + > .name-header { + flex: 1 1 0%; + margin-left: var(--margin-xs); + } + + > .qty-header { + margin-right: var(--margin-lg); + } + + > .rate-amount-header { + margin-right: var(--margin-xs); + text-align: right; + } + } + + .no-item-wrapper { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--gray-50); + border-radius: var(--border-radius-md); + font-size: var(--text-md); + font-weight: 500; + width: 100%; + height: 100%; + } + + > .cart-items-section { + display: flex; + flex-direction: column; + flex: 1 1 0%; + overflow-y: scroll; + + > .cart-item-wrapper { + @extend .pointer-no-select; + display: flex; + align-items: center; + padding: var(--padding-sm); + border-radius: var(--border-radius-md); + + &:hover { + background-color: var(--gray-50); + } + + > .item-image { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: var(--border-radius-md); + color: var(--gray-500); + margin-right: var(--margin-md); + + > img { + @extend .image; + } + } + + > .item-abbr { + @extend .abbr; + font-size: var(--text-lg); + } + + + > .item-name-desc { + @extend .nowrap; + display: flex; + flex-direction: column; + flex: 1 1 0%; + flex-shrink: 1; + + > .item-name { + font-weight: 700; + } + + > .item-desc { + font-size: var(--text-sm); + color: var(--gray-600); + font-weight: 500; + } + } + + > .item-qty-rate { + display: flex; + flex-shrink: 0; + text-align: right; + margin-left: var(--margin-md); + + > .item-qty { + display: flex; + align-items: center; + margin-right: var(--margin-lg); + font-weight: 700; + } + + > .item-rate-amount { + display: flex; + flex-direction: column; + flex-shrink: 0; + text-align: right; + + > .item-rate { + font-weight: 700; + } + + > .item-amount { + font-size: var(--text-md); + font-weight: 600; + } + } + } + + } + } + + > .cart-totals-section { + display: flex; + flex-direction: column; + flex-shrink: 0; + width: 100%; + margin-top: var(--margin-md); + + > .net-total-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 500; + font-size: var(--text-md); + } + + > .taxes-container { + display: none; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 500; + font-size: var(--text-md); + + > .tax-label { + display: flex; + align-items: center; + + > .tax-desc { + margin-left: var(--margin-md); + } + } + } + + > .grand-total-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 700; + font-size: var(--text-lg); + } + + > .checkout-btn { + @extend .primary-action; + background-color: var(--blue-200); + color: white; + } + } + + > .numpad-section { + display: none; + flex-direction: column; + flex-shrink: 0; + margin-top: var(--margin-sm); + padding: var(--padding-sm); + padding-bottom: 0px; + width: 100%; + + > .numpad-totals { + display: flex; + justify-content: space-between; + margin-bottom: var(--margin-md); + font-size: var(--text-md); + font-weight: 700; + } + + > .numpad-container { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: var(--margin-md); + margin-bottom: var(--margin-md); + + > .numpad-btn { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-md); + box-shadow: var(--shadow-sm); + } + + > .col-span-2 { + grid-column: span 2 / span 2; + } + + > .remove-btn { + font-weight: 700; + color: var(--red-500); + } + } + + > .checkout-btn { + @extend .primary-action; + margin: 0px; + margin-bottom: var(--margin-sm); + background-color: var(--blue-200); + color: white; + } + } + } } } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 11453f7cf0..e0a2ee7767 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -43,16 +43,17 @@ erpnext.PointOfSale.ItemCart = class { init_cart_components() { this.$component.append( - `
-
-
-
Item
-
Qty
-
Amount
+ `
+
+
Item Cart
+
+
Item
+
Qty
+
Amount
-
-
-
+
+
+
` ); @@ -71,13 +72,10 @@ erpnext.PointOfSale.ItemCart = class { } make_no_items_placeholder() { - this.$cart_header.addClass('d-none'); + this.$cart_header.css('display', 'none'); this.$cart_items_wrapper.html( - `
-
No items in cart
-
` - ) - this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed'); + `
No items in cart
` + ); } make_cart_totals_section() { @@ -87,30 +85,18 @@ erpnext.PointOfSale.ItemCart = class { `
+ Add Discount
-
-
-
-
Net Total
-
-
-
0.00
-
-
-
-
-
-
Grand Total
-
-
-
0.00
-
-
-
- Checkout -
-
- Edit Cart -
+
+
Net Total
+
0.00
+
+
+
+
Grand Total
+
0.00
+
+
Checkout
+
+ Edit Cart
` ) @@ -136,23 +122,20 @@ erpnext.PointOfSale.ItemCart = class { [ '', '', '', 'col-span-2' ], [ '', '', '', 'col-span-2' ], [ '', '', '', 'col-span-2' ], - [ '', '', '', 'col-span-2 text-bold text-danger' ] + [ '', '', '', 'col-span-2 remove-btn' ] ], fieldnames_map: { 'Quantity': 'qty', 'Discount': 'discount_percentage' } }) this.$numpad_section.prepend( - `
+ `
` ) this.$numpad_section.append( - `
- Checkout -
` + `
Checkout
` ) } @@ -167,10 +150,9 @@ erpnext.PointOfSale.ItemCart = class { }); this.$customer_section.on('click', '.customer-display', function(e) { - // don't triggger the event if .reset-customer-btn btn is clicked which is under .customer-header - if ($(e.target).closest('.reset-customer-btn').length) return; + if ($(this).find('.reset-customer-btn').length == 0) return; - const show = !me.$cart_container.hasClass('d-none'); + const show = me.$cart_container.is(':visible'); me.toggle_customer_info(show); }); @@ -283,20 +265,15 @@ erpnext.PointOfSale.ItemCart = class { toggle_item_highlight(item) { const $cart_item = $(item); - const item_is_highlighted = $cart_item.hasClass("shadow-base"); - if (!item || item_is_highlighted) { + if (!item) { this.item_is_selected = false; - this.$cart_container.find('.cart-item-wrapper').removeClass("shadow-base").css("opacity", "1"); + this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); } else { - $cart_item.addClass("shadow-base"); + $cart_item.css("background-color", "var(--gray-50)"); this.item_is_selected = true; - this.$cart_container.find('.cart-item-wrapper').css("opacity", "1"); - this.$cart_container.find('.cart-item-wrapper').not(item).removeClass("shadow-base").css("opacity", "0.65"); + this.$cart_container.find('.cart-item-wrapper').not(item).css("background-color", ""); } - // highlight with inner shadow-base - // $cart_item.addClass("shadow-base-inner bg-selected"); - // me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-base-inner bg-selected"); } make_customer_selector() { @@ -464,6 +441,7 @@ erpnext.PointOfSale.ItemCart = class { } } + get_customer_image() { const { customer, image } = this.customer_info || {}; if (image) { @@ -485,57 +463,47 @@ erpnext.PointOfSale.ItemCart = class { render_net_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.net-total').html( - `
-
Net Total
-
-
-
${format_currency(value, currency)}
-
` + this.$totals_section.find('.net-total-container').html( + `
Net Total
${format_currency(value, currency)}
` ) - this.$numpad_section.find('.numpad-net-total').html(`Net Total: ${format_currency(value, currency)}`) + this.$numpad_section.find('.numpad-net-total').html( + `
Net Total: ${format_currency(value, currency)}
` + ); } render_grand_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.grand-total').html( - `
-
Grand Total
-
-
-
${format_currency(value, currency)}
-
` + this.$totals_section.find('.grand-total-container').html( + `
Grand Total
${format_currency(value, currency)}
` ) - this.$numpad_section.find('.numpad-grand-total').html(`Grand Total: ${format_currency(value, currency)}`) + this.$numpad_section.find('.numpad-grand-total').html( + `
Grand Total: ${format_currency(value, currency)}
` + ) } render_taxes(value, taxes) { if (taxes.length) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.taxes').html( - `
-
-
Tax Charges
-
- ${ - taxes.map((t, i) => { - let margin_left = ''; - if (i !== 0) margin_left = 'ml-2'; - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `${description}` - }).join('') - } -
+ this.$totals_section.find('.taxes-container').css('display', 'flex').html( + `
+
Tax Charges
+
+ ${ + taxes.map((t, i) => { + let margin_left = ''; + if (i !== 0) margin_left = '10px'; + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `${description}` + }).join('') + }
-
-
${format_currency(value, currency)}
-
-
` +
+
${format_currency(value, currency)}
` ) } else { - this.$totals_section.find('.taxes').html('') + this.$totals_section.find('.taxes-container').css('display', 'none').html(''); } } @@ -564,9 +532,9 @@ erpnext.PointOfSale.ItemCart = class { this.render_cart_item(item_row, $item); } - const no_of_cart_items = this.$cart_items_wrapper.children().length; - no_of_cart_items > 0 && this.highlight_checkout_btn(no_of_cart_items > 0); - + const no_of_cart_items = this.$cart_items_wrapper.find('.cart-item-wrapper').length; + this.highlight_checkout_btn(no_of_cart_items > 0); + this.update_empty_cart_section(no_of_cart_items); } @@ -576,32 +544,33 @@ erpnext.PointOfSale.ItemCart = class { if (!$item_to_update.length) { this.$cart_items_wrapper.append( - `
-
` +
+
` ) $item_to_update = this.get_cart_item(item_data); } $item_to_update.html( - `
-
+ `${get_item_image_html()} +
+
${item_data.item_name}
${get_description_html()}
- ${get_rate_discount_html()} -
` + ${get_rate_discount_html()}` ) set_dynamic_rate_header_width(); this.scroll_to_item($item_to_update); function set_dynamic_rate_header_width() { - const rate_cols = Array.from(me.$cart_items_wrapper.find(".rate-col")); - me.$cart_header.find(".rate-list-header").css("width", ""); - me.$cart_items_wrapper.find(".rate-col").css("width", ""); + const rate_cols = Array.from(me.$cart_items_wrapper.find(".item-rate-amount")); + me.$cart_header.find(".rate-amount-header").css("width", ""); + me.$cart_items_wrapper.find(".item-rate-amount").css("width", ""); let max_width = rate_cols.reduce((max_width, elm) => { if ($(elm).width() > max_width) max_width = $(elm).width(); @@ -611,30 +580,26 @@ erpnext.PointOfSale.ItemCart = class { max_width += 1; if (max_width == 1) max_width = ""; - me.$cart_header.find(".rate-list-header").css("width", max_width); - me.$cart_items_wrapper.find(".rate-col").css("width", max_width); + me.$cart_header.find(".rate-amount-header").css("width", max_width); + me.$cart_items_wrapper.find(".item-rate-amount").css("width", max_width); } function get_rate_discount_html() { if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) { return ` -
-
- ${item_data.qty || 0} -
-
-
${format_currency(item_data.amount, currency)}
-
${format_currency(item_data.rate, currency)}
+
+
${item_data.qty || 0}
+
+
${format_currency(item_data.amount, currency)}
+
${format_currency(item_data.rate, currency)}
` } else { return ` -
-
- ${item_data.qty || 0} -
-
-
${format_currency(item_data.rate, currency)}
+
+
${item_data.qty || 0}
+
+
${format_currency(item_data.rate, currency)}
` } @@ -650,10 +615,19 @@ erpnext.PointOfSale.ItemCart = class { } } item_data.description = frappe.ellipsis(item_data.description, 45); - return `
${item_data.description}
` + return `
${item_data.description}
` } return ``; } + + function get_item_image_html() { + const { image, item_name } = item_data; + if (image) { + return `
${image}
` + } else { + return `
${frappe.get_abbr(item_name)}
` + } + } } scroll_to_item($item) { @@ -669,20 +643,23 @@ erpnext.PointOfSale.ItemCart = class { toggle_checkout_btn(show_checkout) { if (show_checkout) { - this.$totals_section.find('.checkout-btn').removeClass('d-none'); - this.$totals_section.find('.edit-cart-btn').addClass('d-none'); + this.$totals_section.find('.checkout-btn').css('display', 'flex'); + this.$totals_section.find('.edit-cart-btn').css('display', 'none'); } else { - this.$totals_section.find('.checkout-btn').addClass('d-none'); - this.$totals_section.find('.edit-cart-btn').removeClass('d-none'); + this.$totals_section.find('.checkout-btn').css('display', 'none'); + this.$totals_section.find('.edit-cart-btn').css('display', 'flex'); } } highlight_checkout_btn(toggle) { - const has_primary_class = this.$totals_section.find('.checkout-btn').hasClass('bg-primary'); - if (toggle && !has_primary_class) { - this.$totals_section.find('.checkout-btn').addClass('bg-primary text-white text-lg'); - } else if (!toggle && has_primary_class) { - this.$totals_section.find('.checkout-btn').removeClass('bg-primary text-white text-lg'); + if (toggle) { + this.$cart_container.find('.checkout-btn').css({ + 'background-color': 'var(--blue-500)' + }); + } else { + this.$cart_container.find('.checkout-btn').css({ + 'background-color': 'var(--blue-200)' + }); } } @@ -690,8 +667,7 @@ erpnext.PointOfSale.ItemCart = class { const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper'); // if cart has items and no item is present - no_of_cart_items > 0 && $no_item_element && $no_item_element.remove() - && this.$cart_items_wrapper.removeClass('mt-4 border-grey border-dashed') && this.$cart_header.removeClass('d-none'); + no_of_cart_items > 0 && $no_item_element && $no_item_element.remove() && this.$cart_header.css('display', 'flex'); no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder(); } @@ -754,36 +730,36 @@ erpnext.PointOfSale.ItemCart = class { } highlight_numpad_btn($btn, curr_action) { - const curr_action_is_highlighted = $btn.hasClass('shadow-base-inner'); + const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn'); const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action); if (!curr_action_is_highlighted) { - $btn.addClass('shadow-base-inner bg-selected'); + $btn.addClass('highlighted-numpad-btn'); } if (this.prev_action === curr_action && curr_action_is_highlighted) { // if Qty is pressed twice - $btn.removeClass('shadow-base-inner bg-selected'); + $btn.removeClass('highlighted-numpad-btn'); } if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) { // Order: Qty -> Rate then remove Qty highlight const prev_btn = $(`[data-button-value='${this.prev_action}']`); - prev_btn.removeClass('shadow-base-inner bg-selected'); + prev_btn.removeClass('highlighted-numpad-btn'); } if (!curr_action_is_action || curr_action === 'done') { // if numbers are clicked setTimeout(() => { - $btn.removeClass('shadow-base-inner bg-selected'); - }, 100); + $btn.removeClass('highlighted-numpad-btn'); + }, 200); } } toggle_numpad(show) { if (show) { - this.$totals_section.addClass('d-none'); - this.$numpad_section.removeClass('d-none'); + this.$totals_section.css('display', 'none'); + this.$numpad_section.css('display', 'flex'); } else { - this.$totals_section.removeClass('d-none'); - this.$numpad_section.addClass('d-none'); + this.$totals_section.css('display', 'flex'); + this.$numpad_section.css('display', 'none'); } this.reset_numpad(); } @@ -791,7 +767,7 @@ erpnext.PointOfSale.ItemCart = class { reset_numpad() { this.numpad_value = ''; this.prev_action = undefined; - this.$numpad_section.find('.shadow-base-inner').removeClass('shadow-base-inner bg-selected'); + this.$numpad_section.find('.highlighted-numpad-btn').removeClass('highlighted-numpad-btn'); } toggle_numpad_field_edit(fieldname) { @@ -804,7 +780,7 @@ erpnext.PointOfSale.ItemCart = class { if (show) { const { customer } = this.customer_info || {}; - this.$cart_container.addClass('d-none'); + this.$cart_container.css('display', 'none'); this.$customer_section.css({ 'height': '100%', 'padding-top': '0px', @@ -842,7 +818,7 @@ erpnext.PointOfSale.ItemCart = class { this.fetch_customer_transactions(); } else { - this.$cart_container.removeClass('d-none'); + this.$cart_container.css('display', 'flex'); this.$customer_section.css({ 'height': '', 'padding-top': '', @@ -988,20 +964,18 @@ erpnext.PointOfSale.ItemCart = class { this.update_totals_section(frm); if(frm.doc.docstatus === 1) { - this.$totals_section.find('.checkout-btn').addClass('d-none'); - this.$totals_section.find('.edit-cart-btn').addClass('d-none'); - this.$totals_section.find('.grand-total').removeClass('border-b-grey'); + this.$totals_section.find('.checkout-btn').css('display', 'none'); + this.$totals_section.find('.edit-cart-btn').css('display', 'none'); } else { - this.$totals_section.find('.checkout-btn').removeClass('d-none'); - this.$totals_section.find('.edit-cart-btn').addClass('d-none'); - this.$totals_section.find('.grand-total').addClass('border-b-grey'); + this.$totals_section.find('.checkout-btn').css('display', 'flex'); + this.$totals_section.find('.edit-cart-btn').css('display', 'none'); } this.toggle_component(true); } toggle_component(show) { - show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); + show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } } diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js index 4b8e841805..edde7d84df 100644 --- a/erpnext/selling/page/point_of_sale/pos_number_pad.js +++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js @@ -25,14 +25,13 @@ erpnext.PointOfSale.NumberPad = class { const fieldname = fieldnames && fieldnames[number] ? fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number; - return a2 + `
${number}
` + return a2 + `
${number}
` }, '') }, ''); } this.wrapper.html( - `
+ `
${get_keys()}
` ) From fc3315a6d7938212009955802cbd7181083e0025 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 22:59:10 +0530 Subject: [PATCH 127/283] feat: pos item details with new ui --- erpnext/public/scss/point-of-sale.scss | 114 +++++++++++++++++- .../page/point_of_sale/pos_item_cart.js | 11 +- .../page/point_of_sale/pos_item_details.js | 83 ++++++------- .../page/point_of_sale/pos_item_selector.js | 16 +-- 4 files changed, 160 insertions(+), 64 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 37ca8fd13d..573b9dc2b7 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -119,7 +119,6 @@ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--margin-lg); - flex: 1 1 0%; padding: var(--padding-lg); padding-top: var(--padding-xs); @@ -471,6 +470,13 @@ background-color: var(--blue-200); color: white; } + + > .edit-cart-btn { + @extend .primary-action; + display: none; + background-color: var(--gray-100); + font-weight: 500; + } } > .numpad-section { @@ -542,7 +548,7 @@ > .invoice-name-date { display: flex; flex-direction: column; - justify-content: end; + justify-content: flex-end; > .invoice-name { @extend .nowrap; @@ -580,4 +586,108 @@ } } } + + > .item-details-container { + @extend .pos-card; + grid-column: span 4 / span 4; + display: none; + flex-direction: column; + padding: var(--padding-lg); + padding-top: var(--padding-md); + + > .item-details-header { + display: flex; + justify-content: space-between; + margin-bottom: var(--margin-md); + + > .close-btn { + @extend .pointer-no-select; + } + } + + > .item-display { + display: flex; + + > .item-name-desc-price { + flex: 1 1 0%; + display: flex; + flex-direction: column; + justify-content: flex-end; + margin-right: var(--margin-md); + + > .item-name { + font-size: var(--text-3xl); + font-weight: 600; + } + + > .item-desc { + font-size: var(--text-md); + font-weight: 500; + } + + > .item-price { + font-size: var(--text-3xl); + font-weight: 700; + } + } + + > .item-image { + display: flex; + align-items: center; + justify-content: center; + width: 11rem; + height: 11rem; + border-radius: var(--border-radius-md); + margin-left: var(--margin-md); + color: var(--gray-500); + + > img { + @extend .image; + } + + > .item-abbr { + @extend .abbr; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius-md); + font-size: var(--text-3xl); + width: 100%; + height: 100%; + } + } + } + + > .discount-section { + display: flex; + align-items: center; + margin-bottom: var(--margin-sm); + + > .item-rate { + font-weight: 500; + margin-right: var(--margin-sm); + text-decoration: line-through; + } + + > .item-discount { + padding: 3px var(--padding-sm); + border-radius: var(--border-radius-sm); + background-color: var(--green-100); + color: var(--green-700); + font-size: var(--text-sm); + font-weight: 700; + } + } + + > .form-container { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--padding-xs); + + > .auto-fetch-btn { + @extend .pointer-no-select; + margin: auto var(--margin-xs); + } + } + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index e0a2ee7767..b560ba4a87 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -95,9 +95,7 @@ erpnext.PointOfSale.ItemCart = class {
0.00
Checkout
-
- Edit Cart -
` +
Edit Cart
` ) this.$add_discount_elem = this.$component.find(".add-discount"); @@ -176,7 +174,7 @@ erpnext.PointOfSale.ItemCart = class { }); this.$component.on('click', '.checkout-btn', function() { - if (!$(this).hasClass('bg-primary')) return; + if ($(this).attr('style').indexOf('--blue-500') == -1) return; me.events.checkout(); me.toggle_checkout_btn(false); @@ -265,8 +263,9 @@ erpnext.PointOfSale.ItemCart = class { toggle_item_highlight(item) { const $cart_item = $(item); + const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);"; - if (!item) { + if (!item || item_is_highlighted) { this.item_is_selected = false; this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); } else { @@ -522,7 +521,7 @@ erpnext.PointOfSale.ItemCart = class { const $item = this.get_cart_item(item); if (remove_item) { - $item && $item.remove(); + $item && $item.next().remove() && $item.remove(); } else { const { item_code, batch_no, uom } = item; const search_field = batch_no ? 'batch_no' : 'item_code'; diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js index a4de9f165d..546154345b 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -16,35 +16,36 @@ erpnext.PointOfSale.ItemDetails = class { prepare_dom() { this.wrapper.append( - `
` + `
` ) - this.$component = this.wrapper.find('.item-details'); + this.$component = this.wrapper.find('.item-details-container'); } init_child_components() { this.$component.html( - `
-
-
ITEM DETAILS
-
Close
+ `
+
Item Details
+
+ + +
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
STOCK DETAILS
-
-
` +
+
+
+
` ) this.$item_name = this.$component.find('.item-name'); - this.$item_description = this.$component.find('.item-description'); + this.$item_description = this.$component.find('.item-desc'); this.$item_price = this.$component.find('.item-price'); this.$item_image = this.$component.find('.item-image'); this.$form_container = this.$component.find('.form-container'); @@ -52,7 +53,7 @@ erpnext.PointOfSale.ItemDetails = class { } toggle_item_details_section(item) { - const { item_code, batch_no, uom } = this.current_item; + const { item_code, batch_no, uom } = this.current_item; const item_code_is_same = item && item_code === item.item_code; const batch_is_same = item && batch_no == item.batch_no; const uom_is_same = item && uom === item.uom; @@ -104,11 +105,11 @@ erpnext.PointOfSale.ItemDetails = class { } render_dom(item) { - let { item_code ,item_name, description, image, price_list_rate } = item; + let { item_name, description, image, price_list_rate } = item; function get_description_html() { if (description) { - description = description.indexOf('...') === -1 && description.length > 75 ? description.substr(0, 73) + '...' : description; + description = description.indexOf('...') === -1 && description.length > 140 ? description.substr(0, 139) + '...' : description; return description; } return ``; @@ -118,11 +119,9 @@ erpnext.PointOfSale.ItemDetails = class { this.$item_description.html(get_description_html()); this.$item_price.html(format_currency(price_list_rate, this.currency)); if (image) { - this.$item_image.html( - `${image}` - ); + this.$item_image.html(`${image}`); } else { - this.$item_image.html(frappe.get_abbr(item_code)); + this.$item_image.html(`
${frappe.get_abbr(item_name)}
`); } } @@ -130,12 +129,8 @@ erpnext.PointOfSale.ItemDetails = class { render_discount_dom(item) { if (item.discount_percentage) { this.$dicount_section.html( - `
- ${format_currency(item.price_list_rate, this.currency)} -
-
- ${item.discount_percentage}% off -
` + `
${format_currency(item.price_list_rate, this.currency)}
+
${item.discount_percentage}% off
` ) this.$item_price.html(format_currency(item.rate, this.currency)); } else { @@ -149,9 +144,7 @@ erpnext.PointOfSale.ItemDetails = class { fields_to_display.forEach((fieldname, idx) => { this.$form_container.append( - `
-
-
` + `
` ) const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname); @@ -185,22 +178,15 @@ erpnext.PointOfSale.ItemDetails = class { make_auto_serial_selection_btn(item) { if (item.has_serial_no) { - this.$form_container.append( - `
` - ) if (!item.has_batch_no) { this.$form_container.append( `
` ) } this.$form_container.append( - `
- Auto Fetch Serial Numbers -
` + `
Auto Fetch Serial Numbers
` ) - this.$form_container.find('.serial_no-control').find('textarea').css('height', '9rem'); - this.$form_container.find('.serial_no-control').parent().addClass('row-span-2'); + this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem'); } } @@ -294,8 +280,13 @@ erpnext.PointOfSale.ItemDetails = class { } frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => { - const field_control = me[`${fieldname}_control`]; - if (field_control) { + const field_control = this[`${fieldname}_control`]; + const { item_code, batch_no, uom } = this.current_item; + const item_code_is_same = item_code === item_row.item_code; + const batch_is_same = batch_no == item_row.batch_no; + const uom_is_same = uom === item_row.uom; + + if (field_control && item_code_is_same && batch_is_same && uom_is_same) { field_control.set_value(value); cur_pos.update_cart_html(item_row); } @@ -409,6 +400,6 @@ erpnext.PointOfSale.ItemDetails = class { } toggle_component(show) { - show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); + show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 4195d93d4e..0bac84481d 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -248,20 +248,16 @@ erpnext.PointOfSale.ItemSelector = class { resize_selector(minimize) { minimize ? - this.$component.find('.search-field').removeClass('mr-8') : - this.$component.find('.search-field').addClass('mr-8'); - - minimize ? - this.$component.find('.filter-section').addClass('flex-col') : - this.$component.find('.filter-section').removeClass('flex-col'); + this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : + this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))'); minimize ? - this.$component.removeClass('col-span-6').addClass('col-span-2') : - this.$component.removeClass('col-span-2').addClass('col-span-6') + this.$component.css('grid-column', 'span 2 / span 2') : + this.$component.css('grid-column', 'span 6 / span 6') minimize ? - this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') : - this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4') + this.$items_container.css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : + this.$items_container.css('grid-template-columns', 'repeat(4, minmax(0, 1fr))') } toggle_component(show) { From 7202e9f65845c602878381c167a870fea54b83a5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 13 Nov 2020 17:33:20 +0530 Subject: [PATCH 128/283] refactor: pos payment with new ui --- erpnext/public/scss/point-of-sale.scss | 199 ++++++++++++++++-- .../page/point_of_sale/pos_controller.js | 6 +- .../page/point_of_sale/pos_item_cart.js | 57 +++-- .../page/point_of_sale/pos_item_selector.js | 2 +- .../selling/page/point_of_sale/pos_payment.js | 162 +++++++------- 5 files changed, 297 insertions(+), 129 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 573b9dc2b7..61be422e48 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -5,12 +5,12 @@ section { min-height: 45rem; + height: calc(100vh - 200px); max-height: calc(100vh - 200px); } .frappe-control { margin: 0 !important; - padding: 5px 5px; width: 100%; } @@ -63,9 +63,7 @@ align-items: center; justify-content: center; padding: var(--padding-sm); - margin: var(--margin-xs); margin-top: var(--margin-sm); - margin-bottom: var(--margin-xs); border-radius: var(--border-radius-md); font-size: var(--text-lg); font-weight: 700; @@ -91,21 +89,21 @@ position: sticky; top: -1px; z-index: 1; - padding: var(--padding-md); + padding: var(--padding-lg); padding-bottom: var(--padding-sm); align-items: center; > .label { @extend .label; grid-column: span 4 / span 4; - padding: var(--padding-xs); - padding-top: 0px; + padding-bottom: var(--padding-xs); } > .search-field { grid-column: span 5 / span 5; display: flex; align-items: center; + margin-right: var(--padding-sm); } > .item-group-field { @@ -177,7 +175,7 @@ @extend .pos-card; display: flex; flex-direction: column; - padding: var(--padding-md); + padding: var(--padding-md) var(--padding-lg); > .customer-field { display: flex; @@ -288,36 +286,33 @@ position: absolute; display: flex; flex-direction: column; - padding: var(--padding-md); + padding: var(--padding-lg); width: 100%; height: 100%; > .cart-label { @extend .label; padding-bottom: var(--padding-md); - padding-left: var(--margin-sm); } > .cart-header { display: flex; width: 100%; font-size: var(--text-md); - padding-left: var(--padding-xs); - padding-right: var(--padding-xs); padding-bottom: var(--padding-md); > .name-header { flex: 1 1 0%; - margin-left: var(--margin-xs); } > .qty-header { margin-right: var(--margin-lg); + text-align: center; } > .rate-amount-header { - margin-right: var(--margin-xs); text-align: right; + margin-right: var(--margin-sm); } } @@ -429,11 +424,38 @@ width: 100%; margin-top: var(--margin-md); + > .add-discount-wrapper { + @extend .pointer-no-select; + display: none; + align-items: center; + border-radius: var(--border-radius-md); + border: 1px dashed var(--gray-500); + padding: var(--padding-sm) var(--padding-md); + margin-bottom: var(--margin-sm); + + > .add-discount-field { + width: 100%; + } + + > .discount-icon { + margin-right: var(--margin-sm); + } + + > .edit-discount-btn { + padding: 3px var(--padding-sm); + border-radius: var(--border-radius-sm); + background-color: var(--green-100); + color: var(--green-700); + font-size: var(--text-sm); + font-weight: 700; + } + } + > .net-total-container { display: flex; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); } @@ -442,7 +464,7 @@ display: none; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); @@ -460,7 +482,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 700; font-size: var(--text-lg); } @@ -474,8 +496,15 @@ > .edit-cart-btn { @extend .primary-action; display: none; - background-color: var(--gray-100); + background-color: var(--gray-300); font-weight: 500; + transition: all 0.15s ease-in-out; + + &:hover { + background-color: var(--gray-600); + color: white; + font-weight: 700; + } } } @@ -682,12 +711,144 @@ > .form-container { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: var(--padding-xs); + column-gap: var(--padding-md); > .auto-fetch-btn { @extend .pointer-no-select; - margin: auto var(--margin-xs); + margin: var(--margin-xs); } } } + + > .payment-container { + @extend .pos-card; + grid-column: span 6 / span 6; + display: none; + flex-direction: column; + padding: var(--padding-lg); + + .border-primary { + border: 1px solid var(--blue-500); + } + + .submit-order-btn { + @extend .primary-action; + background-color: var(--blue-500); + color: white; + } + + .section-label { + @extend .label; + @extend .pointer-no-select; + margin-bottom: var(--margin-md); + } + + > .payment-modes { + display: flex; + margin-bottom: var(--margin-md); + overflow-x: scroll; + overflow-y: hidden; + + > .payment-mode-wrapper { + min-width: 40%; + padding: var(--padding-xs); + + > .mode-of-payment { + @extend .pos-card; + @extend .pointer-no-select; + padding: var(--padding-md) var(--padding-lg); + + > .pay-amount { + display: inline; + float: right; + font-weight: 700; + } + + > .mode-of-payment-control { + display: none; + align-items: center; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + } + + > .loyalty-amount-name { + display: none; + float: right; + font-weight: 700; + } + + > .cash-shortcuts { + display: none; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--margin-sm); + font-size: var(--text-sm); + text-align: center; + + > .shortcut { + @extend .pointer-no-select; + border-radius: var(--border-radius-sm); + background-color: var(--gray-100); + font-weight: 500; + padding: var(--padding-xs) var(--padding-sm); + transition: all 0.15s ease-in-out; + + &:hover { + background-color: var(--gray-300); + } + } + } + } + } + } + + .invoice-fields { + display: none; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--padding-md); + } + + > .totals-section { + display: flex; + margin-top: auto; + margin-bottom: var(--margin-sm); + justify-content: center; + flex-direction: column; + + > .totals { + display: flex; + padding-top: var(--padding-md); + background-color: var(--gray-100); + justify-content: center; + padding: var(--padding-md); + border-radius: var(--border-radius-md); + + > .col { + flex-grow: 1; + text-align: center; + + > .total-label { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + } + + > .value { + font-size: var(--text-2xl); + font-weight: 700; + } + } + + > .seperator-y { + margin-left: var(--margin-sm); + margin-right: var(--margin-sm); + border-right: 1px solid var(--gray-300); + } + } + + > .number-pad { + display: none; + } + } + + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 8cbd59086c..4875ec061f 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -356,10 +356,10 @@ erpnext.PointOfSale.Controller = class { toggle_other_sections: (show) => { if (show) { - this.item_details.$component.hasClass('d-none') ? '' : this.item_details.$component.addClass('d-none'); - this.item_selector.$component.addClass('d-none'); + this.item_details.$component.is(':visible') ? this.item_details.$component.css('display', 'none') : ''; + this.item_selector.$component.css('display', 'none'); } else { - this.item_selector.$component.removeClass('d-none'); + this.item_selector.$component.css('display', 'flex'); } }, diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index b560ba4a87..867cd2df63 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -78,12 +78,23 @@ erpnext.PointOfSale.ItemCart = class { ); } + get_discount_icon() { + return ( + ` + + + + + ` + ); + } + make_cart_totals_section() { this.$totals_section = this.$component.find('.cart-totals-section'); this.$totals_section.append( - `
- + Add Discount + `
+ ${this.get_discount_icon()} Add Discount
Net Total
@@ -98,7 +109,7 @@ erpnext.PointOfSale.ItemCart = class {
Edit Cart
` ) - this.$add_discount_elem = this.$component.find(".add-discount"); + this.$add_discount_elem = this.$component.find(".add-discount-wrapper"); } make_cart_numpad() { @@ -178,19 +189,15 @@ erpnext.PointOfSale.ItemCart = class { me.events.checkout(); me.toggle_checkout_btn(false); - - me.$add_discount_elem.removeClass("d-none"); }); this.$totals_section.on('click', '.edit-cart-btn', () => { this.events.edit_cart(); this.toggle_checkout_btn(true); - - this.$add_discount_elem.addClass("d-none"); }); - this.$component.on('click', '.add-discount', () => { - const can_edit_discount = this.$add_discount_elem.find('.edit-discount').length; + this.$component.on('click', '.add-discount-wrapper', () => { + const can_edit_discount = this.$add_discount_elem.find('.edit-discount-btn').length; if(!this.discount_field || can_edit_discount) this.show_discount_control(); }); @@ -244,10 +251,10 @@ erpnext.PointOfSale.ItemCart = class { this.$component.find(".edit-cart-btn").click() } }); - this.$component.find(".add-discount").attr("title", `${ctrl_label}+D`); + this.$component.find(".add-discount-wrapper").attr("title", `${ctrl_label}+D`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+d", - action: () => this.$component.find(".add-discount").click(), + action: () => this.$component.find(".add-discount-wrapper").click(), condition: () => this.$add_discount_elem.is(":visible"), description: __("Add Order Discount"), ignore_inputs: true, @@ -292,7 +299,6 @@ erpnext.PointOfSale.ItemCart = class { label: __('Customer'), fieldtype: 'Link', options: 'Customer', - input_class: 'input-xs', placeholder: __('Search by customer name, phone, email.'), get_query: () => query, onchange: function() { @@ -351,9 +357,9 @@ erpnext.PointOfSale.ItemCart = class { } show_discount_control() { - this.$add_discount_elem.removeClass("pr-4 pl-4"); + this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }) this.$add_discount_elem.html( - `
` + `
` ); const me = this; @@ -362,14 +368,19 @@ erpnext.PointOfSale.ItemCart = class { label: __('Discount'), fieldtype: 'Data', placeholder: __('Enter discount percentage.'), + input_class: 'input-xs', onchange: function() { const frm = me.events.get_frm(); - if (this.value.length || this.value === 0) { + if (flt(this.value) != 0) { frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value)); me.hide_discount_control(this.value); } else { frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', 0); - me.$add_discount_elem.html(`+ Add Discount`); + me.$add_discount_elem.css({ + 'border': '1px dashed var(--gray-500)', + 'padding': 'var(--padding-sm) var(--padding-md)' + }); + me.$add_discount_elem.html(`${me.get_discount_icon()} Add Discount`); me.discount_field = undefined; } }, @@ -383,21 +394,19 @@ erpnext.PointOfSale.ItemCart = class { hide_discount_control(discount) { if (!discount) { - this.$add_discount_elem.removeClass("pr-4 pl-4"); + this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }); this.$add_discount_elem.html( - `
` + `
` ); } else { - this.$add_discount_elem.addClass('pr-4 pl-4'); this.$add_discount_elem.html( - ` -
+
${String(discount).bold()}% off -
- ` +
` ); } } @@ -652,10 +661,12 @@ erpnext.PointOfSale.ItemCart = class { highlight_checkout_btn(toggle) { if (toggle) { + this.$add_discount_elem.css('display', 'flex'); this.$cart_container.find('.checkout-btn').css({ 'background-color': 'var(--blue-500)' }); } else { + this.$add_discount_elem.css('display', 'none'); this.$cart_container.find('.checkout-btn').css({ 'background-color': 'var(--blue-200)' }); diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 0bac84481d..e2a2365e32 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -261,6 +261,6 @@ erpnext.PointOfSale.ItemSelector = class { } toggle_component(show) { - show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); + show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index e4d8965ac2..9f8c2dff1d 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -18,38 +18,28 @@ erpnext.PointOfSale.Payment = class { prepare_dom() { this.wrapper.append( - `
-
-
- PAYMENT METHOD -
-
-
-
-
-
-
-
-
-
-
-
-
- Complete Order -
-
-
-
+ `
+ +
+
+
+
+
+
+
Complete Order
` ) - this.$component = this.wrapper.find('.payment-section'); + this.$component = this.wrapper.find('.payment-container'); this.$payment_modes = this.$component.find('.payment-modes'); - this.$totals_remarks = this.$component.find('.totals-remarks'); + this.$totals_section = this.$component.find('.totals-section'); this.$totals = this.$component.find('.totals'); - this.$remarks = this.$component.find('.remarks'); this.$numpad = this.$component.find('.number-pad'); - this.$invoice_details_section = this.$component.find('.invoice-details-section'); + this.$invoice_fields_section = this.$component.find('.invoice-fields-section'); } make_invoice_fields_control() { @@ -57,13 +47,16 @@ erpnext.PointOfSale.Payment = class { const fields = doc.invoice_fields; if (!fields.length) return; - this.$invoice_details_section.html( - `
- ADDITIONAL INFORMATION + this.$invoice_fields_section.html( + ` -
` +
` ); - this.$invoice_fields = this.$invoice_details_section.find('.invoice-fields'); + this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); const frm = this.events.get_frm(); fields.forEach(df => { @@ -127,9 +120,9 @@ erpnext.PointOfSale.Payment = class { this.selected_mode.set_value(this.numpad_value); function highlight_numpad_btn($btn) { - $btn.addClass('shadow-inner bg-selected'); + $btn.addClass('shadow-base-inner bg-selected'); setTimeout(() => { - $btn.removeClass('shadow-inner bg-selected'); + $btn.removeClass('shadow-base-inner bg-selected'); }, 100); } } @@ -142,13 +135,16 @@ erpnext.PointOfSale.Payment = class { // if clicked element doesn't have .mode-of-payment class then return if (!$(e.target).is(mode_clicked)) return; + // const scrollRight = mode_clicked.offset().right - me.$payment_modes.offset().right + me.$payment_modes.scrollRight(); + // me.$payment_modes.animate({ scrollRight }); + const mode = mode_clicked.attr('data-mode'); // hide all control fields and shortcuts - $(`.mode-of-payment-control`).addClass('d-none'); - $(`.cash-shortcuts`).addClass('d-none'); - me.$payment_modes.find(`.pay-amount`).removeClass('d-none'); - me.$payment_modes.find(`.loyalty-amount-name`).addClass('d-none'); + $(`.mode-of-payment-control`).css('display', 'none'); + $(`.cash-shortcuts`).css('display', 'none'); + me.$payment_modes.find(`.pay-amount`).css('display', 'inline'); + me.$payment_modes.find(`.loyalty-amount-name`).css('display', 'none'); // remove highlight from all mode-of-payments $('.mode-of-payment').removeClass('border-primary'); @@ -161,10 +157,10 @@ erpnext.PointOfSale.Payment = class { } else { // clicked one is not selected then select it mode_clicked.addClass('border-primary'); - mode_clicked.find('.mode-of-payment-control').removeClass('d-none'); - mode_clicked.find('.cash-shortcuts').removeClass('d-none'); - me.$payment_modes.find(`.${mode}-amount`).addClass('d-none'); - me.$payment_modes.find(`.${mode}-name`).removeClass('d-none'); + mode_clicked.find('.mode-of-payment-control').css('display', 'flex'); + mode_clicked.find('.cash-shortcuts').css('display', 'grid'); + me.$payment_modes.find(`.${mode}-amount`).css('display', 'none'); + me.$payment_modes.find(`.${mode}-name`).css('display', 'inline'); me.toggle_numpad(true); me.selected_mode = me[`${mode}_control`]; @@ -198,7 +194,7 @@ erpnext.PointOfSale.Payment = class { me.selected_mode.set_value(value); }) - this.$component.on('click', '.submit-order', () => { + this.$component.on('click', '.submit-order-btn', () => { const doc = this.events.get_frm().doc; const paid_amount = doc.paid_amount; const items = doc.items; @@ -217,9 +213,9 @@ erpnext.PointOfSale.Payment = class { this.update_totals_section(frm.doc); // need to re calculate cash shortcuts after discount is applied - const is_cash_shortcuts_invisible = this.$payment_modes.find('.cash-shortcuts').hasClass('d-none'); + const is_cash_shortcuts_invisible = !this.$payment_modes.find('.cash-shortcuts').is(':visible'); this.attach_cash_shortcuts(frm.doc); - !is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').removeClass('d-none'); + !is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').css('display', 'grid'); }) frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => { @@ -236,28 +232,28 @@ erpnext.PointOfSale.Payment = class { } }); - this.$component.on('click', '.invoice-details-section', function(e) { + this.$component.on('click', '.invoice-fields-section', function(e) { if ($(e.target).closest('.invoice-fields').length) return; - me.$payment_modes.addClass('d-none'); - me.$invoice_fields.toggleClass("d-none"); + me.$payment_modes.css('display', 'none'); + me.$invoice_fields.css('display', 'grid'); me.toggle_numpad(false); }); this.$component.on('click', '.payment-section', () => { - this.$invoice_fields.addClass("d-none"); - this.$payment_modes.toggleClass('d-none'); + this.$invoice_fields.css('display', 'none'); + this.$payment_modes.css('display', 'flex'); this.toggle_numpad(true); }) } attach_shortcuts() { const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; - this.$component.find('.submit-order').attr("title", `${ctrl_label}+Enter`); + this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`); frappe.ui.keys.on("ctrl+enter", () => { const payment_is_visible = this.$component.is(":visible"); const active_mode = this.$payment_modes.find(".border-primary"); if (payment_is_visible && active_mode.length) { - this.$component.find('.submit-order').click(); + this.$component.find('.submit-order-btn').click(); } }); @@ -287,15 +283,13 @@ erpnext.PointOfSale.Payment = class { } toggle_numpad(show) { - if (show) { - this.$numpad.removeClass('d-none'); - this.$remarks.addClass('d-none'); - this.$totals_remarks.addClass('w-60 justify-center').removeClass('justify-end w-full'); - } else { - this.$numpad.addClass('d-none'); - this.$remarks.removeClass('d-none'); - this.$totals_remarks.removeClass('w-60 justify-center').addClass('justify-end w-full'); - } + // if (show) { + // this.$numpad.css('display', 'flex'); + // this.$totals_section.addClass('w-60 justify-center').removeClass('justify-end w-full'); + // } else { + // this.$numpad.css('display', 'none'); + // this.$totals_section.removeClass('w-60 justify-center').addClass('justify-end w-full'); + // } } render_payment_section() { @@ -327,7 +321,7 @@ erpnext.PointOfSale.Payment = class { fieldtype: 'Data', onchange: function() {} }, - parent: this.$totals_remarks.find(`.remarks`), + parent: this.$totals_section.find(`.remarks`), render_input: true, }); this[`remark_control`].set_value(''); @@ -348,12 +342,11 @@ erpnext.PointOfSale.Payment = class { const amount = p.amount > 0 ? format_currency(p.amount, currency) : ''; return ( - `
-
+ `
+
${p.mode_of_payment} -
${amount}
-
+
${amount}
+
` ) @@ -405,12 +398,10 @@ erpnext.PointOfSale.Payment = class { this.$payment_modes.find('.cash-shortcuts').remove(); this.$payment_modes.find('[data-payment-type="Cash"]').find('.mode-of-payment-control').after( - `
+ `
${ shortcuts.map(s => { - return `
- ${format_currency(s, currency, 0)} -
` + return `
${format_currency(s, currency, 0)}
` }).join('') }
` @@ -457,13 +448,12 @@ erpnext.PointOfSale.Payment = class { const margin = this.$payment_modes.children().length % 2 === 0 ? 'pr-2' : 'pl-2'; const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : ''; this.$payment_modes.append( - `
-
+ `
+
Redeem Loyalty Points -
${amount}
-
${loyalty_program}
-
+
${amount}
+
${loyalty_program}
+
` ) @@ -520,18 +510,24 @@ erpnext.PointOfSale.Payment = class { const label = change ? __('Change') : __('To Be Paid'); this.$totals.html( - `
-
Paid Amount
-
${format_currency(paid_amount, currency)}
+ `
+
Grand Total
+
${format_currency(doc.grand_total, currency)}
-
-
${label}
-
${format_currency(change || remaining, currency)}
+
+
+
Paid Amount
+
${format_currency(paid_amount, currency)}
+
+
+
+
${label}
+
${format_currency(change || remaining, currency)}
` ) } toggle_component(show) { - show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); + show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } } \ No newline at end of file From b0b6aa9124bab869f70022aedf1d3985408123b5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 12:57:06 +0530 Subject: [PATCH 129/283] refactor: past order list and summary with new ui --- erpnext/public/scss/point-of-sale.scss | 249 +++++++++++++- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 43 ++- .../page/point_of_sale/pos_item_selector.js | 2 +- .../page/point_of_sale/pos_past_order_list.js | 51 ++- .../point_of_sale/pos_past_order_summary.js | 310 ++++++------------ 6 files changed, 385 insertions(+), 272 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 61be422e48..60043bf110 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -75,7 +75,14 @@ background-color: var(--gray-50); } + .sticky-element { + position: sticky; + top: -1px; + z-index: 1; + } + > .items-selector { + @extend .pos-card; grid-column: span 6 / span 6; display: flex; flex-direction: column; @@ -83,12 +90,10 @@ overflow-x: hidden; > .filter-section { + @extend .sticky-element; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); background-color: var(--fg-color); - position: sticky; - top: -1px; - z-index: 1; padding: var(--padding-lg); padding-bottom: var(--padding-sm); align-items: center; @@ -124,6 +129,10 @@ @extend .pointer-no-select; border-radius: var(--border-radius-md); box-shadow: var(--shadow-base); + + &:hover { + transform: scale(1.02, 1.02); + } .item-display { display: flex; @@ -180,14 +189,13 @@ > .customer-field { display: flex; align-items: center; + padding-top: var(--padding-xs); } > .customer-details { + @extend .sticky-element; display: flex; flex-direction: column; - position: sticky; - top: -1px; - z-index: 1; background-color: var(--fg-color); > .header { @@ -263,6 +271,7 @@ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: var(--margin-sm); + gap: var(--padding-sm); } > .transactions-label { @@ -445,7 +454,7 @@ padding: 3px var(--padding-sm); border-radius: var(--border-radius-sm); background-color: var(--green-100); - color: var(--green-700); + color: var(--dark-green-500); font-size: var(--text-sm); font-weight: 700; } @@ -462,19 +471,14 @@ > .taxes-container { display: none; - align-items: center; - justify-content: space-between; + flex-direction: column; padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); - > .tax-label { + > .tax-row { display: flex; - align-items: center; - - > .tax-desc { - margin-left: var(--margin-md); - } + justify-content: space-between; } } @@ -702,7 +706,7 @@ padding: 3px var(--padding-sm); border-radius: var(--border-radius-sm); background-color: var(--green-100); - color: var(--green-700); + color: var(--dark-green-500); font-size: var(--text-sm); font-weight: 700; } @@ -849,6 +853,219 @@ display: none; } } + } + > .past-order-list { + @extend .pos-card; + grid-column: span 4 / span 4; + display: none; + flex-direction: column; + overflow-y: scroll; + overflow-x: hidden; + + > .filter-section { + @extend .sticky-element; + display: flex; + flex-direction: column; + background-color: var(--fg-color); + padding: var(--padding-lg); + + > .search-field { + width: 100%; + display: flex; + align-items: center; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + } + + > .status-field { + width: 100%; + display: flex; + align-items: center; + } + } + + > .invoices-container { + padding: var(--padding-lg); + padding-top: 0px; + } + } + + > .past-order-summary { + // @extend .pos-card; + display: none; + grid-column: span 6 / span 6; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--padding-lg); + + > .no-summary-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background-color: var(--gray-50); + font-weight: 500; + border-radius: var(--border-radius-md); + } + + > .invoice-summary-wrapper { + @extend .pos-card; + display: none; + position: relative; + width: 31rem; + height: 100%; + + > .abs-container { + position: absolute; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + padding: var(--padding-lg); + + > .upper-section { + display: flex; + justify-content: space-between; + width: 100%; + margin-bottom: var(--margin-md); + + > .left-section { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-end; + padding-right: var(--padding-sm); + + > .customer-name { + font-size: var(--text-2xl); + font-weight: 700; + } + + > .customer-email { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + } + + > .cashier { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + margin-top: auto; + } + } + + > .right-section { + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; + + > .paid-amount { + font-size: var(--text-2xl); + font-weight: 700; + } + + > .invoice-name { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + margin-bottom: var(--margin-sm); + } + } + } + + > .summary-container { + display: flex; + flex-direction: column; + border-radius: var(--border-radius-md); + background-color: var(--gray-50); + margin: var(--margin-md) 0px; + + > .summary-row-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + } + + > .taxes-wrapper { + display: flex; + flex-direction: column; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + + > .tax-row { + display: flex; + justify-content: space-between; + } + } + + > .item-row-wrapper { + display: flex; + align-items: center; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + + > .item-name { + @extend .nowrap; + font-weight: 500; + margin-right: var(--margin-md); + } + + > .item-qty { + font-weight: 500; + margin-left: auto; + } + + > .item-rate-disc { + display: flex; + text-align: right; + margin-left: var(--margin-md); + + > .item-disc { + color: var(--dark-green-500); + } + + > .item-rate { + font-weight: 500; + margin-left: var(--margin-md); + } + } + } + + > .grand-total { + // font-size: var(--text-lg); + font-weight: 700; + padding: var(--padding-md); + } + + > .payments { + font-weight: 700; + } + } + + + > .summary-btns { + display: flex; + justify-content: space-between; + + > .summary-btn { + flex: 1; + margin: 0px var(--margin-xs); + } + + > .new-btn { + background-color: var(--blue-500); + color:white; + font-weight: 500; + } + } + } + } } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 4875ec061f..9a7ddd9a57 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -190,7 +190,7 @@ erpnext.PointOfSale.Controller = class { } toggle_recent_order() { - const show = this.recent_order_list.$component.hasClass('d-none'); + const show = this.recent_order_list.$component.is(':hidden'); this.toggle_recent_order_list(show); } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 867cd2df63..955111439b 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -159,7 +159,7 @@ erpnext.PointOfSale.ItemCart = class { }); this.$customer_section.on('click', '.customer-display', function(e) { - if ($(this).find('.reset-customer-btn').length == 0) return; + if ($(e.target).closest('.reset-customer-btn').length) return; const show = me.$cart_container.is(':visible'); me.toggle_customer_info(show); @@ -495,20 +495,17 @@ erpnext.PointOfSale.ItemCart = class { if (taxes.length) { const currency = this.events.get_frm().doc.currency; this.$totals_section.find('.taxes-container').css('display', 'flex').html( - `
-
Tax Charges
-
- ${ - taxes.map((t, i) => { - let margin_left = ''; - if (i !== 0) margin_left = '10px'; - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `${description}` - }).join('') - } -
-
-
${format_currency(value, currency)}
` + `${ + taxes.map((t, i) => { + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `
+
+ ${description} +
+
${format_currency(value, currency)}
+
` + }).join('') + }` ) } else { this.$totals_section.find('.taxes-container').css('display', 'none').html(''); @@ -926,11 +923,12 @@ erpnext.PointOfSale.ItemCart = class { res.forEach(invoice => { const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma"); - let indicator_color = ''; - - if (in_list(['Paid', 'Consolidated'], invoice.status)) (indicator_color = 'green'); - if (invoice.status === 'Draft') (indicator_color = 'red'); - if (invoice.status === 'Return') (indicator_color = 'grey'); + let indicator_color = { + 'Paid': 'green', + 'Draft': 'red', + 'Return': 'gray', + 'Consolidated': 'blue' + }; transaction_container.append( `
@@ -943,8 +941,9 @@ erpnext.PointOfSale.ItemCart = class { ${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
- - ${invoice.status} + + ${invoice.status} +
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index e2a2365e32..38fe645a65 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -17,7 +17,7 @@ erpnext.PointOfSale.ItemSelector = class { prepare_dom() { this.wrapper.append( - `
+ `
All Items
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index b256247924..166d9cf0ce 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -14,17 +14,13 @@ erpnext.PointOfSale.PastOrderList = class { prepare_dom() { this.wrapper.append( - `
-
-
-
-
-
-
-
RECENT ORDERS
-
-
+ `
+
+
Recent Orders
+
+
+
` ); @@ -77,10 +73,6 @@ erpnext.PointOfSale.PastOrderList = class { this.status_field.set_value('Draft'); } - toggle_component(show) { - show ? this.$component.removeClass('d-none') && this.refresh_list() : this.$component.addClass('d-none'); - } - refresh_list() { frappe.dom.freeze(); this.events.reset_summary(); @@ -106,23 +98,26 @@ erpnext.PointOfSale.PastOrderList = class { get_invoice_html(invoice) { const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma"); return ( - `
-
-
${invoice.name}
-
-
- - - - ${invoice.customer} -
+ `
+
+
${invoice.name}
+
+ + + + ${invoice.customer}
-
-
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
-
${posting_datetime}
+
+
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
+
${posting_datetime}
-
` +
+
` ); } + + toggle_component(show) { + show ? this.$component.css('display', 'flex') && this.refresh_list() : this.$component.css('display', 'none'); + } }; \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 6fd4c26bea..2e94ecefa1 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -8,85 +8,39 @@ erpnext.PointOfSale.PastOrderSummary = class { init_component() { this.prepare_dom(); - this.init_child_components(); + this.init_email_print_dialog(); this.bind_events(); this.attach_shortcuts(); } prepare_dom() { this.wrapper.append( - `
-
-
-
Select an invoice to load summary data
-
+ `
+
+ Select an invoice to load summary data
-
-
+
+
+
+
Items
+
+
Totals
+
+
Payments
+
+
+
` ); this.$component = this.wrapper.find('.past-order-summary'); - this.$summary_wrapper = this.$component.find('.summary-wrapper'); - this.$summary_container = this.$component.find('.summary-container'); - } - - init_child_components() { - this.init_upper_section(); - this.init_items_summary(); - this.init_totals_summary(); - this.init_payments_summary(); - this.init_summary_buttons(); - this.init_email_print_dialog(); - } - - init_upper_section() { - this.$summary_container.append( - `
` - ); - + this.$summary_wrapper = this.$component.find('.invoice-summary-wrapper'); + this.$summary_container = this.$component.find('.abs-container'); this.$upper_section = this.$summary_container.find('.upper-section'); - } - - init_items_summary() { - this.$summary_container.append( - `
-
ITEMS
-
-
` - ); - - this.$items_summary_container = this.$summary_container.find('.items-summary-container'); - } - - init_totals_summary() { - this.$summary_container.append( - `
-
TOTALS
-
-
` - ); - - this.$totals_summary_container = this.$summary_container.find('.summary-totals-container'); - } - - init_payments_summary() { - this.$summary_container.append( - `
-
PAYMENTS
-
-
` - ); - - this.$payment_summary_container = this.$summary_container.find('.payments-summary-container'); - } - - init_summary_buttons() { - this.$summary_container.append( - `
` - ); - + this.$items_container = this.$summary_container.find('.items-container'); + this.$totals_container = this.$summary_container.find('.totals-container'); + this.$payment_container = this.$summary_container.find('.payments-container'); this.$summary_btns = this.$summary_container.find('.summary-btns'); } @@ -121,132 +75,88 @@ erpnext.PointOfSale.PastOrderSummary = class { } get_upper_section_html(doc) { - const { status } = doc; let indicator_color = ''; + const { status } = doc; + let indicator_color = ''; in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green'); status === 'Draft' && (indicator_color = 'red'); status === 'Return' && (indicator_color = 'grey'); - return `
-
${doc.customer}
-
${this.customer_email}
-
Sold by: ${doc.owner}
+ return `
+
${doc.customer}
+
${this.customer_email}
+
Sold by: ${doc.owner}
-
-
${format_currency(doc.paid_amount, doc.currency)}
-
-
${doc.name}
-
${doc.status}
-
+
+ +
${doc.name}
+ ${doc.status}
`; } + get_item_html(doc, item_data) { + return `
+
${item_data.item_name}
+
${item_data.qty || 0}
+
${get_rate_discount_html()}
+
`; + + function get_rate_discount_html() { + if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) { + return `(${item_data.discount_percentage}% off) +
${format_currency(item_data.rate, doc.currency)}
`; + } else { + return `
${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}
`; + } + } + } + get_discount_html(doc) { if (doc.discount_amount) { - return `
-
-
- Discount -
- (${doc.additional_discount_percentage} %) -
-
-
${format_currency(doc.discount_amount, doc.currency)}
-
-
`; + return `
+
Discount (${doc.additional_discount_percentage} %)
+
${format_currency(doc.discount_amount, doc.currency)}
+
`; } else { return ``; } } get_net_total_html(doc) { - return `
-
-
- Net Total -
-
-
-
${format_currency(doc.net_total, doc.currency)}
-
+ return `
+
Net Total
+
${format_currency(doc.net_total, doc.currency)}
`; } get_taxes_html(doc) { - const taxes = doc.taxes.map((t, i) => { - let margin_left = ''; - if (i !== 0) margin_left = 'ml-2'; - return `${t.description} @${t.rate}%`; - }).join(''); + if (!doc.taxes.length) return ''; return ` -
-
-
Tax Charges
-
${taxes}
-
-
-
- ${format_currency(doc.base_total_taxes_and_charges, doc.currency)} -
-
+
+ ${ + doc.taxes.map((t, i) => { + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `
+
${description}
+
${format_currency(t.tax_amount_after_discount_amount, doc.currency)}
+
` + }).join('') + }
`; } get_grand_total_html(doc) { - return `
-
-
- Grand Total -
-
-
-
${format_currency(doc.grand_total, doc.currency)}
-
+ return `
+
Grand Total
+
${format_currency(doc.grand_total, doc.currency)}
`; } - get_item_html(doc, item_data) { - return `
-
- ${item_data.qty || 0} -
-
-
- ${item_data.item_name} -
-
-
- ${get_rate_discount_html()} -
-
`; - - function get_rate_discount_html() { - if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) { - return ` - (${item_data.discount_percentage}% off) - -
- ${format_currency(item_data.rate, doc.currency)} -
`; - } else { - return `
- ${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)} -
`; - } - } - } - get_payment_html(doc, payment) { - return `
-
-
- ${payment.mode_of_payment} -
-
-
-
${format_currency(payment.amount, doc.currency)}
-
+ return `
+
${payment.mode_of_payment}
+
${format_currency(payment.amount, doc.currency)}
`; } @@ -254,22 +164,22 @@ erpnext.PointOfSale.PastOrderSummary = class { this.$summary_container.on('click', '.return-btn', () => { this.events.process_return(this.doc.name); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').removeClass('d-none'); - this.$summary_wrapper.addClass('d-none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); + this.$summary_wrapper.css('display', 'none'); }); this.$summary_container.on('click', '.edit-btn', () => { this.events.edit_order(this.doc.name); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').removeClass('d-none'); - this.$summary_wrapper.addClass('d-none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); + this.$summary_wrapper.css('display', 'none'); }); this.$summary_container.on('click', '.new-btn', () => { this.events.new_order(); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').removeClass('d-none'); - this.$summary_wrapper.addClass('d-none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); + this.$summary_wrapper.css('display', 'none'); }); this.$summary_container.on('click', '.email-btn', () => { @@ -312,10 +222,6 @@ erpnext.PointOfSale.PastOrderSummary = class { }); } - toggle_component(show) { - show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); - } - send_email() { const frm = this.events.get_frm(); const recipients = this.email_dialog.get_values().recipients; @@ -338,8 +244,10 @@ erpnext.PointOfSale.PastOrderSummary = class { if(!r.exc) { frappe.utils.play_sound("email"); if(r.message["emails_not_sent_to"]) { - frappe.msgprint(__("Email not sent to {0} (unsubscribed / disabled)", - [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) ); + frappe.msgprint(__( + "Email not sent to {0} (unsubscribed / disabled)", + [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ] + )); } else { frappe.show_alert({ message: __('Email sent successfully.'), @@ -361,9 +269,7 @@ erpnext.PointOfSale.PastOrderSummary = class { m.visible_btns.forEach(b => { const class_name = b.split(' ')[0].toLowerCase(); this.$summary_btns.append( - `
- ${b} -
` + `
${b}
` ); }); } @@ -372,28 +278,20 @@ erpnext.PointOfSale.PastOrderSummary = class { } show_summary_placeholder() { - this.$summary_wrapper.addClass("d-none"); - this.$component.find('.no-summary-placeholder').removeClass('d-none'); + this.$summary_wrapper.css('display', 'none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); } switch_to_post_submit_summary() { - // switch to full width view - this.$component.removeClass('col-span-6').addClass('col-span-10'); - this.$summary_wrapper.removeClass('w-66').addClass('w-40'); - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').addClass('d-none'); - this.$summary_wrapper.removeClass('d-none'); + this.$component.find('.no-summary-placeholder').css('display', 'none'); + this.$summary_wrapper.css('display', 'flex'); } switch_to_recent_invoice_summary() { - // switch full width view with 60% view - this.$component.removeClass('col-span-10').addClass('col-span-6'); - this.$summary_wrapper.removeClass('w-40').addClass('w-66'); - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').addClass('d-none'); - this.$summary_wrapper.removeClass('d-none'); + this.$component.find('.no-summary-placeholder').css('display', 'none'); + this.$summary_wrapper.css('display', 'flex'); } get_condition_btn_map(after_submission) { @@ -410,8 +308,8 @@ erpnext.PointOfSale.PastOrderSummary = class { load_summary_of(doc, after_submission=false) { this.$summary_wrapper.removeClass("d-none"); - after_submission ? - this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); + // after_submission ? + // this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); this.doc = doc; @@ -437,19 +335,19 @@ erpnext.PointOfSale.PastOrderSummary = class { } attach_items_info(doc) { - this.$items_summary_container.html(''); - doc.items.forEach(item => { + this.$items_container.html(''); + doc.items.forEach((item, i) => { const item_dom = this.get_item_html(doc, item); - this.$items_summary_container.append(item_dom); + this.$items_container.append(item_dom); }); } attach_payments_info(doc) { - this.$payment_summary_container.html(''); + this.$payment_container.html(''); doc.payments.forEach(p => { if (p.amount) { const payment_dom = this.get_payment_html(doc, p); - this.$payment_summary_container.append(payment_dom); + this.$payment_container.append(payment_dom); } }); if (doc.redeem_loyalty_points && doc.loyalty_amount) { @@ -457,20 +355,24 @@ erpnext.PointOfSale.PastOrderSummary = class { mode_of_payment: 'Loyalty Points', amount: doc.loyalty_amount, }); - this.$payment_summary_container.append(payment_dom); + this.$payment_container.append(payment_dom); } } attach_totals_info(doc) { - this.$totals_summary_container.html(''); + this.$totals_container.html(''); - const discount_dom = this.get_discount_html(doc); const net_total_dom = this.get_net_total_html(doc); const taxes_dom = this.get_taxes_html(doc); + const discount_dom = this.get_discount_html(doc); const grand_total_dom = this.get_grand_total_html(doc); - this.$totals_summary_container.append(discount_dom); - this.$totals_summary_container.append(net_total_dom); - this.$totals_summary_container.append(taxes_dom); - this.$totals_summary_container.append(grand_total_dom); + this.$totals_container.append(net_total_dom); + this.$totals_container.append(taxes_dom); + this.$totals_container.append(discount_dom); + this.$totals_container.append(grand_total_dom); + } + + toggle_component(show) { + show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); } }; \ No newline at end of file From d0d6fc2e175977dfc011faa8a4e803887763379d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 13:33:40 +0530 Subject: [PATCH 130/283] fix: cannot open past order summary --- erpnext/public/css/pos.css | 217 ------------------ erpnext/public/scss/point-of-sale.scss | 5 +- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 12 +- .../page/point_of_sale/pos_item_selector.js | 6 +- .../point_of_sale/pos_past_order_summary.js | 30 +-- .../selling/page/point_of_sale/pos_payment.js | 4 +- 7 files changed, 30 insertions(+), 246 deletions(-) delete mode 100644 erpnext/public/css/pos.css diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css deleted file mode 100644 index 47f577131a..0000000000 --- a/erpnext/public/css/pos.css +++ /dev/null @@ -1,217 +0,0 @@ -[data-route="point-of-sale"] .layout-main-section { border: none; font-size: 12px; } -[data-route="point-of-sale"] .layout-main-section-wrapper { margin-bottom: 0; } -[data-route="point-of-sale"] .pos-items-wrapper { max-height: calc(100vh - 210px); } -:root { --border-color: #d1d8dd; --text-color: #8d99a6; --primary: #5e64ff; } -[data-route="point-of-sale"] .flex { display: flex; } -[data-route="point-of-sale"] .grid { display: grid; } -[data-route="point-of-sale"] .absolute { position: absolute; } -[data-route="point-of-sale"] .relative { position: relative; } -[data-route="point-of-sale"] .abs-center { top: 50%; left: 50%; transform: translate(-50%, -50%); } -[data-route="point-of-sale"] .inline { display: inline; } -[data-route="point-of-sale"] .float-right { float: right; } -[data-route="point-of-sale"] .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } -[data-route="point-of-sale"] .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } -[data-route="point-of-sale"] .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } -[data-route="point-of-sale"] .grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -[data-route="point-of-sale"] .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } -[data-route="point-of-sale"] .grid-cols-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } -[data-route="point-of-sale"] .gap-2 { grid-gap: 0.5rem; gap: 0.5rem; } -[data-route="point-of-sale"] .gap-4 { grid-gap: 1rem; gap: 1rem; } -[data-route="point-of-sale"] .gap-6 { grid-gap: 1.25rem; gap: 1.25rem; } -[data-route="point-of-sale"] .gap-8 { grid-gap: 1.5rem; gap: 1.5rem; } -[data-route="point-of-sale"] .row-gap-2 { grid-row-gap: 0.5rem; row-gap: 0.5rem; } -[data-route="point-of-sale"] .col-gap-4 { grid-column-gap: 1rem; column-gap: 1rem; } -[data-route="point-of-sale"] .col-span-2 { grid-column: span 2 / span 2; } -[data-route="point-of-sale"] .col-span-3 { grid-column: span 3 / span 3; } -[data-route="point-of-sale"] .col-span-4 { grid-column: span 4 / span 4; } -[data-route="point-of-sale"] .col-span-6 { grid-column: span 6 / span 6; } -[data-route="point-of-sale"] .col-span-10 { grid-column: span 10 / span 10; } -[data-route="point-of-sale"] .row-span-2 { grid-row: span 2 / span 2; } -[data-route="point-of-sale"] .grid-auto-row { grid-auto-rows: 5.5rem; } -[data-route="point-of-sale"] .d-none { display: none; } -[data-route="point-of-sale"] .flex-wrap { flex-wrap: wrap; } -[data-route="point-of-sale"] .flex-row { flex-direction: row; } -[data-route="point-of-sale"] .flex-col { flex-direction: column; } -[data-route="point-of-sale"] .flex-row-rev { flex-direction: row-reverse; } -[data-route="point-of-sale"] .flex-col-rev { flex-direction: column-reverse; } -[data-route="point-of-sale"] .flex-1 { flex: 1 1 0%; } -[data-route="point-of-sale"] .items-center { align-items: center; } -[data-route="point-of-sale"] .items-end { align-items: flex-end; } -[data-route="point-of-sale"] .f-grow-1 { flex-grow: 1; } -[data-route="point-of-sale"] .f-grow-2 { flex-grow: 2; } -[data-route="point-of-sale"] .f-grow-3 { flex-grow: 3; } -[data-route="point-of-sale"] .f-grow-4 { flex-grow: 4; } -[data-route="point-of-sale"] .f-shrink-0 { flex-shrink: 0; } -[data-route="point-of-sale"] .f-shrink-1 { flex-shrink: 1; } -[data-route="point-of-sale"] .f-shrink-2 { flex-shrink: 2; } -[data-route="point-of-sale"] .f-shrink-3 { flex-shrink: 3; } -[data-route="point-of-sale"] .shadow { box-shadow: 0 0px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 2px 0 rgba(0, 0, 0, 0.06); } -[data-route="point-of-sale"] .shadow-sm { box-shadow: 0 0.5px 3px 0 rgba(0, 0, 0, 0.125); } -[data-route="point-of-sale"] .shadow-inner { box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1); } -[data-route="point-of-sale"] .rounded { border-radius: 0.3rem; } -[data-route="point-of-sale"] .rounded-b { border-bottom-left-radius: 0.3rem; border-bottom-right-radius: 0.3rem; } -[data-route="point-of-sale"] .p-8 { padding: 2rem; } -[data-route="point-of-sale"] .p-16 { padding: 4rem; } -[data-route="point-of-sale"] .p-32 { padding: 8rem; } -[data-route="point-of-sale"] .p-6 { padding: 1.5rem; } -[data-route="point-of-sale"] .p-4 { padding: 1rem; } -[data-route="point-of-sale"] .p-3 { padding: 0.75rem; } -[data-route="point-of-sale"] .p-2 { padding: 0.5rem; } -[data-route="point-of-sale"] .m-8 { margin: 2rem; } -[data-route="point-of-sale"] .p-1 { padding: 0.25rem; } -[data-route="point-of-sale"] .pr-0 { padding-right: 0rem; } -[data-route="point-of-sale"] .pl-0 { padding-left: 0rem; } -[data-route="point-of-sale"] .pt-0 { padding-top: 0rem; } -[data-route="point-of-sale"] .pb-0 { padding-bottom: 0rem; } -[data-route="point-of-sale"] .mr-0 { margin-right: 0rem; } -[data-route="point-of-sale"] .ml-0 { margin-left: 0rem; } -[data-route="point-of-sale"] .mt-0 { margin-top: 0rem; } -[data-route="point-of-sale"] .mb-0 { margin-bottom: 0rem; } -[data-route="point-of-sale"] .pr-2 { padding-right: 0.5rem; } -[data-route="point-of-sale"] .pl-2 { padding-left: 0.5rem; } -[data-route="point-of-sale"] .pt-2 { padding-top: 0.5rem; } -[data-route="point-of-sale"] .pb-2 { padding-bottom: 0.5rem; } -[data-route="point-of-sale"] .pr-3 { padding-right: 0.75rem; } -[data-route="point-of-sale"] .pl-3 { padding-left: 0.75rem; } -[data-route="point-of-sale"] .pt-3 { padding-top: 0.75rem; } -[data-route="point-of-sale"] .pb-3 { padding-bottom: 0.75rem; } -[data-route="point-of-sale"] .pr-4 { padding-right: 1rem; } -[data-route="point-of-sale"] .pl-4 { padding-left: 1rem; } -[data-route="point-of-sale"] .pt-4 { padding-top: 1rem; } -[data-route="point-of-sale"] .pb-4 { padding-bottom: 1rem; } -[data-route="point-of-sale"] .mr-4 { margin-right: 1rem; } -[data-route="point-of-sale"] .ml-4 { margin-left: 1rem; } -[data-route="point-of-sale"] .mt-4 { margin-top: 1rem; } -[data-route="point-of-sale"] .mb-4 { margin-bottom: 1rem; } -[data-route="point-of-sale"] .mr-2 { margin-right: 0.5rem; } -[data-route="point-of-sale"] .ml-2 { margin-left: 0.5rem; } -[data-route="point-of-sale"] .mt-2 { margin-top: 0.5rem; } -[data-route="point-of-sale"] .mb-2 { margin-bottom: 0.5rem; } -[data-route="point-of-sale"] .mr-1 { margin-right: 0.25rem; } -[data-route="point-of-sale"] .ml-1 { margin-left: 0.25rem; } -[data-route="point-of-sale"] .mt-1 { margin-top: 0.25rem; } -[data-route="point-of-sale"] .mb-1 { margin-bottom: 0.25rem; } -[data-route="point-of-sale"] .mr-auto { margin-right: auto; } -[data-route="point-of-sale"] .ml-auto { margin-left: auto; } -[data-route="point-of-sale"] .mt-auto { margin-top: auto; } -[data-route="point-of-sale"] .mb-auto { margin-bottom: auto; } -[data-route="point-of-sale"] .pr-6 { padding-right: 1.5rem; } -[data-route="point-of-sale"] .pl-6 { padding-left: 1.5rem; } -[data-route="point-of-sale"] .pt-6 { padding-top: 1.5rem; } -[data-route="point-of-sale"] .pb-6 { padding-bottom: 1.5rem; } -[data-route="point-of-sale"] .mr-6 { margin-right: 1.5rem; } -[data-route="point-of-sale"] .ml-6 { margin-left: 1.5rem; } -[data-route="point-of-sale"] .mt-6 { margin-top: 1.5rem; } -[data-route="point-of-sale"] .mb-6 { margin-bottom: 1.5rem; } -[data-route="point-of-sale"] .mr-8 { margin-right: 2rem; } -[data-route="point-of-sale"] .ml-8 { margin-left: 2rem; } -[data-route="point-of-sale"] .mt-8 { margin-top: 2rem; } -[data-route="point-of-sale"] .mb-8 { margin-bottom: 2rem; } -[data-route="point-of-sale"] .pr-8 { padding-right: 2rem; } -[data-route="point-of-sale"] .pl-8 { padding-left: 2rem; } -[data-route="point-of-sale"] .pt-8 { padding-top: 2rem; } -[data-route="point-of-sale"] .pb-8 { padding-bottom: 2rem; } -[data-route="point-of-sale"] .pr-16 { padding-right: 4rem; } -[data-route="point-of-sale"] .pl-16 { padding-left: 4rem; } -[data-route="point-of-sale"] .pt-16 { padding-top: 4rem; } -[data-route="point-of-sale"] .pb-16 { padding-bottom: 4rem; } -[data-route="point-of-sale"] .w-full { width: 100%; } -[data-route="point-of-sale"] .h-full { height: 100%; } -[data-route="point-of-sale"] .w-quarter { width: 25%; } -[data-route="point-of-sale"] .w-half { width: 50%; } -[data-route="point-of-sale"] .w-66 { width: 66.66%; } -[data-route="point-of-sale"] .w-33 { width: 33.33%; } -[data-route="point-of-sale"] .w-60 { width: 60%; } -[data-route="point-of-sale"] .w-40 { width: 40%; } -[data-route="point-of-sale"] .w-fit { width: fit-content; } -[data-route="point-of-sale"] .w-6 { width: 2rem; } -[data-route="point-of-sale"] .h-6 { min-height: 2rem; height: 2rem; } -[data-route="point-of-sale"] .w-8 { width: 2.5rem; } -[data-route="point-of-sale"] .h-8 { min-height: 2.5rem; height: 2.5rem; } -[data-route="point-of-sale"] .w-10 { width: 3rem; } -[data-route="point-of-sale"] .h-10 { min-height:3rem; height: 3rem; } -[data-route="point-of-sale"] .h-12 { min-height: 3.3rem; height: 3.3rem; } -[data-route="point-of-sale"] .w-12 { width: 3.3rem; } -[data-route="point-of-sale"] .h-14 { min-height: 4.2rem; height: 4.2rem; } -[data-route="point-of-sale"] .h-16 { min-height: 4.6rem; height: 4.6rem; } -[data-route="point-of-sale"] .h-18 { min-height: 5rem; height: 5rem; } -[data-route="point-of-sale"] .w-18 { width: 5.4rem; } -[data-route="point-of-sale"] .w-24 { width: 7.2rem; } -[data-route="point-of-sale"] .w-26 { width: 8.4rem; } -[data-route="point-of-sale"] .h-24 { min-height: 7.2rem; height: 7.2rem; } -[data-route="point-of-sale"] .h-32 { min-height: 9.6rem; height: 9.6rem; } -[data-route="point-of-sale"] .w-46 { width: 15rem; } -[data-route="point-of-sale"] .h-46 { min-height:15rem; height: 15rem; } -[data-route="point-of-sale"] .h-100 { height: 100vh; } -[data-route="point-of-sale"] .mx-h-70 { max-height: 67rem; } -[data-route="point-of-sale"] .border-grey-300 { border-color: #e2e8f0; } -[data-route="point-of-sale"] .border-grey { border: 1px solid #d1d8dd; } -[data-route="point-of-sale"] .border-white { border: 1px solid #fff; } -[data-route="point-of-sale"] .border-b-grey { border-bottom: 1px solid #d1d8dd; } -[data-route="point-of-sale"] .border-t-grey { border-top: 1px solid #d1d8dd; } -[data-route="point-of-sale"] .border-r-grey { border-right: 1px solid #d1d8dd; } -[data-route="point-of-sale"] .text-dark-grey { color: #5f5f5f; } -[data-route="point-of-sale"] .text-grey { color: #8d99a6; } -[data-route="point-of-sale"] .text-grey-100 { color: #d1d8dd; } -[data-route="point-of-sale"] .text-grey-200 { color: #a0aec0; } -[data-route="point-of-sale"] .bg-green-200 { background-color: #c6f6d5; } -[data-route="point-of-sale"] .text-bold { font-weight: bold; } -[data-route="point-of-sale"] .italic { font-style: italic; } -[data-route="point-of-sale"] .font-weight-450 { font-weight: 450; } -[data-route="point-of-sale"] .justify-around { justify-content: space-around; } -[data-route="point-of-sale"] .justify-between { justify-content: space-between; } -[data-route="point-of-sale"] .justify-center { justify-content: center; } -[data-route="point-of-sale"] .justify-end { justify-content: flex-end; } -[data-route="point-of-sale"] .bg-white { background-color: white; } -[data-route="point-of-sale"] .bg-light-grey { background-color: #f0f4f7; } -[data-route="point-of-sale"] .bg-grey-100 { background-color: #f7fafc; } -[data-route="point-of-sale"] .bg-grey-200 { background-color: #edf2f7; } -[data-route="point-of-sale"] .bg-grey { background-color: #f4f5f6; } -[data-route="point-of-sale"] .text-center { text-align: center; } -[data-route="point-of-sale"] .text-right { text-align: right; } -[data-route="point-of-sale"] .text-sm { font-size: 1rem; } -[data-route="point-of-sale"] .text-md-0 { font-size: 1.25rem; } -[data-route="point-of-sale"] .text-md { font-size: 1.4rem; } -[data-route="point-of-sale"] .text-lg { font-size: 1.6rem; } -[data-route="point-of-sale"] .text-xl { font-size: 2.2rem; } -[data-route="point-of-sale"] .text-2xl { font-size: 2.8rem; } -[data-route="point-of-sale"] .text-2-5xl { font-size: 3rem; } -[data-route="point-of-sale"] .text-3xl { font-size: 3.8rem; } -[data-route="point-of-sale"] .text-6xl { font-size: 4.8rem; } -[data-route="point-of-sale"] .line-through { text-decoration: line-through; } -[data-route="point-of-sale"] .text-primary { color: #5e64ff; } -[data-route="point-of-sale"] .text-white { color: #fff; } -[data-route="point-of-sale"] .text-green-500 { color: #48bb78; } -[data-route="point-of-sale"] .bg-primary { background-color: #5e64ff; } -[data-route="point-of-sale"] .border-primary { border-color: #5e64ff; } -[data-route="point-of-sale"] .text-danger { color: #e53e3e; } -[data-route="point-of-sale"] .scroll-x { overflow-x: scroll;overflow-y: hidden; } -[data-route="point-of-sale"] .scroll-y { overflow-y: scroll;overflow-x: hidden; } -[data-route="point-of-sale"] .overflow-hidden { overflow: hidden; } -[data-route="point-of-sale"] .whitespace-nowrap { white-space: nowrap; } -[data-route="point-of-sale"] .sticky { position: sticky; top: -1px; } -[data-route="point-of-sale"] .bg-white { background-color: #fff; } -[data-route="point-of-sale"] .bg-selected { background-color: #fffdf4; } -[data-route="point-of-sale"] .border-dashed { border-width:1px; border-style: dashed; } -[data-route="point-of-sale"] .z-100 { z-index: 100; } - -[data-route="point-of-sale"] .frappe-control { margin: 0 !important; width: 100%; } -[data-route="point-of-sale"] .form-control { font-size: 12px; } -[data-route="point-of-sale"] .form-group { margin: 0 !important; } -[data-route="point-of-sale"] .pointer { cursor: pointer; } -[data-route="point-of-sale"] .no-select { user-select: none; } -[data-route="point-of-sale"] .item-wrapper:hover { transform: scale(1.02, 1.02); } -[data-route="point-of-sale"] .hover-underline:hover { text-decoration: underline; } -[data-route="point-of-sale"] .item-wrapper { transition: scale 0.2s ease-in-out; } -[data-route="point-of-sale"] .cart-items-section .cart-item-wrapper:not(:first-child) { border-top: none; } -[data-route="point-of-sale"] .customer-transactions .invoice-wrapper:not(:first-child) { border-top: none; } - -[data-route="point-of-sale"] .payment-summary-wrapper:last-child { border-bottom: none; } -[data-route="point-of-sale"] .item-summary-wrapper:last-child { border-bottom: none; } -[data-route="point-of-sale"] .total-summary-wrapper:last-child { border-bottom: none; } -[data-route="point-of-sale"] .invoices-container .invoice-wrapper:last-child { border-bottom: none; } -[data-route="point-of-sale"] .new-btn { background-color: #5e64ff; color: white; border: none;} -[data-route="point-of-sale"] .summary-btns:last-child { margin-right: 0px; } -[data-route="point-of-sale"] ::-webkit-scrollbar { width: 1px } - -[data-route="point-of-sale"] .indicator.grey::before { background-color: #8d99a6; } \ No newline at end of file diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 60043bf110..8291020622 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -108,7 +108,7 @@ grid-column: span 5 / span 5; display: flex; align-items: center; - margin-right: var(--padding-sm); + margin: 0px var(--margin-sm); } > .item-group-field { @@ -898,7 +898,7 @@ flex-direction: column; align-items: center; justify-content: center; - padding: var(--padding-lg); + // padding: var(--padding-lg); > .no-summary-placeholder { display: flex; @@ -1002,6 +1002,7 @@ > .tax-row { display: flex; justify-content: space-between; + font-size: var(--text-md); } } diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 9a7ddd9a57..13c37493ce 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -388,7 +388,7 @@ erpnext.PointOfSale.Controller = class { this.order_summary.load_summary_of(doc); }); }, - reset_summary: () => this.order_summary.show_summary_placeholder() + reset_summary: () => this.order_summary.toggle_summary_placeholder(true) } }) } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 955111439b..a30f5b884d 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -170,7 +170,7 @@ erpnext.PointOfSale.ItemCart = class { me.toggle_item_highlight(this); - const payment_section_hidden = me.$totals_section.find('.edit-cart-btn').hasClass('d-none'); + const payment_section_hidden = !me.$totals_section.find('.edit-cart-btn').is(':visible'); if (!payment_section_hidden) { // payment section is visible // edit cart first and then open item details section @@ -239,7 +239,7 @@ erpnext.PointOfSale.ItemCart = class { frappe.ui.keys.add_shortcut({ shortcut: "ctrl+enter", action: () => this.$component.find(".checkout-btn").click(), - condition: () => this.$component.is(":visible") && this.$totals_section.find('.edit-cart-btn').hasClass('d-none'), + condition: () => this.$component.is(":visible") && !this.$totals_section.find('.edit-cart-btn').is(':visible'), description: __("Checkout Order / Submit Order / New Order"), ignore_inputs: true, page: cur_page.page.page @@ -247,8 +247,9 @@ erpnext.PointOfSale.ItemCart = class { this.$component.find(".edit-cart-btn").attr("title", `${ctrl_label}+E`); frappe.ui.keys.on("ctrl+e", () => { const item_cart_visible = this.$component.is(":visible"); - if (item_cart_visible && this.$totals_section.find('.checkout-btn').hasClass('d-none')) { - this.$component.find(".edit-cart-btn").click() + const checkout_btn_invisible = !this.$totals_section.find('.checkout-btn').is('visible'); + if (item_cart_visible && checkout_btn_invisible) { + this.$component.find(".edit-cart-btn").click(); } }); this.$component.find(".add-discount-wrapper").attr("title", `${ctrl_label}+D`); @@ -638,6 +639,9 @@ erpnext.PointOfSale.ItemCart = class { scroll_to_item($item) { if ($item.length === 0) return; const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop(); + console.log($item.offset()); + console.log(this.$cart_items_wrapper.offset()); + console.log(scrollTop); this.$cart_items_wrapper.animate({ scrollTop }); } diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 38fe645a65..3588e308c8 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -248,9 +248,13 @@ erpnext.PointOfSale.ItemSelector = class { resize_selector(minimize) { minimize ? - this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : + this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))'); + minimize ? + this.$component.find('.search-field').css('margin', 'var(--margin-sm) 0px') : + this.$component.find('.search-field').css('margin', '0px var(--margin-sm)'); + minimize ? this.$component.css('grid-column', 'span 2 / span 2') : this.$component.css('grid-column', 'span 6 / span 6') diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 2e94ecefa1..8d1cd3195a 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -277,21 +277,14 @@ erpnext.PointOfSale.PastOrderSummary = class { this.$summary_btns.children().last().removeClass('mr-4'); } - show_summary_placeholder() { - this.$summary_wrapper.css('display', 'none'); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); - } - - switch_to_post_submit_summary() { - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').css('display', 'none'); - this.$summary_wrapper.css('display', 'flex'); - } - - switch_to_recent_invoice_summary() { - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').css('display', 'none'); - this.$summary_wrapper.css('display', 'flex'); + toggle_summary_placeholder(show) { + if (show) { + this.$summary_wrapper.css('display', 'none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); + } else { + this.$summary_wrapper.css('display', 'flex'); + this.$component.find('.no-summary-placeholder').css('display', 'none'); + } } get_condition_btn_map(after_submission) { @@ -308,12 +301,11 @@ erpnext.PointOfSale.PastOrderSummary = class { load_summary_of(doc, after_submission=false) { this.$summary_wrapper.removeClass("d-none"); - // after_submission ? - // this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); + this.toggle_summary_placeholder(false) this.doc = doc; - this.attach_basic_info(doc); + this.attach_document_info(doc); this.attach_items_info(doc); @@ -326,7 +318,7 @@ erpnext.PointOfSale.PastOrderSummary = class { this.add_summary_btns(condition_btns_map); } - attach_basic_info(doc) { + attach_document_info(doc) { frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => { this.customer_email = message.email_id || ''; const upper_section_dom = this.get_upper_section_html(doc); diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 9f8c2dff1d..5354ac17ef 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -135,8 +135,8 @@ erpnext.PointOfSale.Payment = class { // if clicked element doesn't have .mode-of-payment class then return if (!$(e.target).is(mode_clicked)) return; - // const scrollRight = mode_clicked.offset().right - me.$payment_modes.offset().right + me.$payment_modes.scrollRight(); - // me.$payment_modes.animate({ scrollRight }); + const scrollLeft = mode_clicked.offset().left - me.$payment_modes.offset().left + me.$payment_modes.scrollLeft(); + me.$payment_modes.animate({ scrollLeft }); const mode = mode_clicked.attr('data-mode'); From fada84cc4777aadff14ab496b31b9ece5d7a89a6 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 13:40:46 +0530 Subject: [PATCH 131/283] fix: remove console and asset loading --- erpnext/public/scss/point-of-sale.scss | 2 +- erpnext/selling/page/point_of_sale/pos_controller.js | 7 +------ erpnext/selling/page/point_of_sale/pos_item_cart.js | 3 --- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 8291020622..6d094561a7 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -1042,7 +1042,7 @@ > .grand-total { // font-size: var(--text-lg); font-weight: 700; - padding: var(--padding-md); + // padding: var(--padding-md); } > .payments { diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 13c37493ce..5b0a1b6dc8 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -12,12 +12,7 @@ erpnext.PointOfSale.Controller = class { this.wrapper = $(wrapper).find('.layout-main-section'); this.page = wrapper.page; - this.load_assets(); - } - - load_assets() { - // after loading assets first check if opening entry has been made - frappe.require(['assets/erpnext/css/pos.css'], this.check_opening_entry.bind(this)); + this.check_opening_entry(); } fetch_opening_entry() { diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index a30f5b884d..527de00dae 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -639,9 +639,6 @@ erpnext.PointOfSale.ItemCart = class { scroll_to_item($item) { if ($item.length === 0) return; const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop(); - console.log($item.offset()); - console.log(this.$cart_items_wrapper.offset()); - console.log(scrollTop); this.$cart_items_wrapper.animate({ scrollTop }); } From f719380a0ddca883da8e471977aed9c2b38e6899 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 15:51:03 +0530 Subject: [PATCH 132/283] fix: discount and taxes spacing --- erpnext/public/scss/point-of-sale.scss | 17 +++++++------- .../page/point_of_sale/pos_item_cart.js | 22 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 6d094561a7..7769eb1521 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -446,17 +446,15 @@ width: 100%; } - > .discount-icon { + .discount-icon { margin-right: var(--margin-sm); } - > .edit-discount-btn { - padding: 3px var(--padding-sm); - border-radius: var(--border-radius-sm); - background-color: var(--green-100); + .edit-discount-btn { + display: flex; + align-items: center; + font-weight: 500; color: var(--dark-green-500); - font-size: var(--text-sm); - font-weight: 700; } } @@ -472,13 +470,13 @@ > .taxes-container { display: none; flex-direction: column; - padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); > .tax-row { display: flex; justify-content: space-between; + line-height: var(--text-3xl); } } @@ -996,13 +994,14 @@ > .taxes-wrapper { display: flex; flex-direction: column; - padding: var(--padding-sm) var(--padding-md); + // padding: var(--padding-sm) var(--padding-md); // border-bottom: 1px solid var(--gray-300); > .tax-row { display: flex; justify-content: space-between; font-size: var(--text-md); + line-height: var(--text-3xl); } } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 527de00dae..37c2330f22 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -80,11 +80,11 @@ erpnext.PointOfSale.ItemCart = class { get_discount_icon() { return ( - ` - - - - + ` + + + + ` ); } @@ -400,13 +400,13 @@ erpnext.PointOfSale.ItemCart = class { `
` ); } else { + this.$add_discount_elem.css({ + 'border': '1px dashed var(--dark-green-500)', + 'padding': 'var(--padding-sm) var(--padding-md)' + }); this.$add_discount_elem.html( - ` - - -
- ${String(discount).bold()}% off + `
+ ${this.get_discount_icon()} Additional ${String(discount).bold()}% discount applied
` ); } From fa0ef218dd91d898d25b50761885b04aa7b2e1bc Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:05:55 +0530 Subject: [PATCH 133/283] fix: customer details spacing --- erpnext/public/scss/point-of-sale.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 7769eb1521..b9f6f6bd97 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -270,13 +270,14 @@ > .customer-fields-container { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - margin-top: var(--margin-sm); - gap: var(--padding-sm); + margin-top: var(--margin-md); + column-gap: var(--padding-sm); + row-gap: var(--padding-xs); } > .transactions-label { @extend .label; - margin-top: var(--margin-sm); + margin-top: var(--margin-md); margin-bottom: var(--margin-sm); } } @@ -579,13 +580,12 @@ > .invoice-name-date { display: flex; flex-direction: column; - justify-content: flex-end; + justify-content: space-around; > .invoice-name { @extend .nowrap; font-size: var(--text-md); font-weight: 700; - margin-bottom: var(--margin-xs); } > .invoice-date { From 69ef766a93befb5a90ee26cb3796c016f3cb1769 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:38:32 +0530 Subject: [PATCH 134/283] feat: number pad in payment screen --- erpnext/public/scss/point-of-sale.scss | 38 +++++++++++++--- .../selling/page/point_of_sale/pos_payment.js | 44 +++++-------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index b9f6f6bd97..c35ca81a94 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -747,7 +747,8 @@ > .payment-modes { display: flex; - margin-bottom: var(--margin-md); + padding-bottom: var(--padding-sm); + margin-bottom: var(--margin-xs); overflow-x: scroll; overflow-y: hidden; @@ -803,10 +804,37 @@ } } - .invoice-fields { - display: none; - grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: var(--padding-md); + > .fields-numpad-container { + display: flex; + flex: 1; + + > .fields-section { + flex: 1; + } + + > .number-pad { + flex: 1; + display: flex; + justify-content: flex-end; + align-items: flex-end; + + .numpad-container { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--margin-md); + margin-bottom: var(--margin-md); + + > .numpad-btn { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-md); + box-shadow: var(--shadow-sm); + } + } + } } > .totals-section { diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 5354ac17ef..fc8f8d8c70 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -19,17 +19,17 @@ erpnext.PointOfSale.Payment = class { prepare_dom() { this.wrapper.append( `
- +
-
+
+
+ +
+
+
+
-
Complete Order
` @@ -39,7 +39,7 @@ erpnext.PointOfSale.Payment = class { this.$totals_section = this.$component.find('.totals-section'); this.$totals = this.$component.find('.totals'); this.$numpad = this.$component.find('.number-pad'); - this.$invoice_fields_section = this.$component.find('.invoice-fields-section'); + this.$invoice_fields_section = this.$component.find('.fields-section'); } make_invoice_fields_control() { @@ -47,15 +47,6 @@ erpnext.PointOfSale.Payment = class { const fields = doc.invoice_fields; if (!fields.length) return; - this.$invoice_fields_section.html( - ` -
` - ); this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); const frm = this.events.get_frm(); @@ -153,7 +144,6 @@ erpnext.PointOfSale.Payment = class { // clicked one is selected then unselect it mode_clicked.removeClass('border-primary'); me.selected_mode = ''; - me.toggle_numpad(false); } else { // clicked one is not selected then select it mode_clicked.addClass('border-primary'); @@ -161,13 +151,14 @@ erpnext.PointOfSale.Payment = class { mode_clicked.find('.cash-shortcuts').css('display', 'grid'); me.$payment_modes.find(`.${mode}-amount`).css('display', 'none'); me.$payment_modes.find(`.${mode}-name`).css('display', 'inline'); - me.toggle_numpad(true); me.selected_mode = me[`${mode}_control`]; const doc = me.events.get_frm().doc; me.selected_mode?.$input?.get(0).focus(); const current_value = me.selected_mode?.get_value() !current_value && doc.grand_total > doc.paid_amount ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : ''; + + me.numpad_value = ''; } }) @@ -231,19 +222,6 @@ erpnext.PointOfSale.Payment = class { this[`${mode}_control`].set_value(default_mop.amount); } }); - - this.$component.on('click', '.invoice-fields-section', function(e) { - if ($(e.target).closest('.invoice-fields').length) return; - - me.$payment_modes.css('display', 'none'); - me.$invoice_fields.css('display', 'grid'); - me.toggle_numpad(false); - }); - this.$component.on('click', '.payment-section', () => { - this.$invoice_fields.css('display', 'none'); - this.$payment_modes.css('display', 'flex'); - this.toggle_numpad(true); - }) } attach_shortcuts() { From eeee7e94279de65e18149bfd17fe906eaf19b2c8 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:38:49 +0530 Subject: [PATCH 135/283] fix: post submission invoice recipt screen --- .../selling/page/point_of_sale/pos_past_order_summary.js | 6 ++++-- erpnext/selling/page/point_of_sale/pos_payment.js | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 8d1cd3195a..28076db63b 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -299,10 +299,12 @@ erpnext.PointOfSale.PastOrderSummary = class { } load_summary_of(doc, after_submission=false) { - this.$summary_wrapper.removeClass("d-none"); - this.toggle_summary_placeholder(false) + after_submission ? + this.$summary_wrapper.css('grid-column', 'span 10 / span 10') : + this.$summary_wrapper.css('grid-column', 'span 6 / span 6') + this.doc = doc; this.attach_document_info(doc); diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index fc8f8d8c70..6476b4925a 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -46,8 +46,9 @@ erpnext.PointOfSale.Payment = class { frappe.db.get_doc("POS Settings", undefined).then((doc) => { const fields = doc.invoice_fields; if (!fields.length) return; - + this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); + this.$invoice_fields.html(''); const frm = this.events.get_frm(); fields.forEach(df => { From 937dc467a092da7331cb18cc4c02f68164101284 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:12:21 +0530 Subject: [PATCH 136/283] fix: no transactions placeholder --- erpnext/public/scss/point-of-sale.scss | 17 +++++++++++++++-- .../selling/page/point_of_sale/pos_item_cart.js | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index c35ca81a94..f388093548 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -281,6 +281,19 @@ margin-bottom: var(--margin-sm); } } + + > .customer-transactions { + height: 100%; + + > .no-transactions-placeholder { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--gray-50); + border-radius: var(--border-radius-md); + } + } } > .cart-container { @@ -900,7 +913,7 @@ width: 100%; display: flex; align-items: center; - margin-top: var(--margin-sm); + margin-top: var(--margin-md); margin-bottom: var(--margin-xs); } @@ -1022,7 +1035,7 @@ > .taxes-wrapper { display: flex; flex-direction: column; - // padding: var(--padding-sm) var(--padding-md); + padding: 0px var(--padding-md); // border-bottom: 1px solid var(--gray-300); > .tax-row { diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 37c2330f22..efe716de6c 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -914,7 +914,7 @@ erpnext.PointOfSale.ItemCart = class { if (!res.length) { transaction_container.html( - `
No recent transactions found
` + `
No recent transactions found
` ) return; }; @@ -951,7 +951,7 @@ erpnext.PointOfSale.ItemCart = class {
` ) }); - }) + }); } load_invoice() { From 96feae608db8d12ca6560b3921e429eca9727c47 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:53:34 +0530 Subject: [PATCH 137/283] refactor: build point of sale min js --- erpnext/public/build.json | 10 ++++++++++ erpnext/selling/page/point_of_sale/onscan.js | 1 - .../selling/page/point_of_sale/point_of_sale.js | 9 ++++----- .../selling/page/point_of_sale/pos_controller.js | 14 +++----------- .../page/point_of_sale/pos_item_selector.js | 9 +++++++-- erpnext/selling/page/point_of_sale/pos_payment.js | 13 +++++-------- package.json | 1 + yarn.lock | 5 +++++ 8 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 erpnext/selling/page/point_of_sale/onscan.js diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 78128a105d..b12045c4c8 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -56,5 +56,15 @@ "stock/dashboard/item_dashboard.html", "stock/dashboard/item_dashboard_list.html", "stock/dashboard/item_dashboard.js" + ], + "js/point-of-sale.min.js": [ + "selling/page/point_of_sale/pos_item_selector.js", + "selling/page/point_of_sale/pos_item_cart.js", + "selling/page/point_of_sale/pos_item_details.js", + "selling/page/point_of_sale/pos_number_pad.js", + "selling/page/point_of_sale/pos_payment.js", + "selling/page/point_of_sale/pos_past_order_list.js", + "selling/page/point_of_sale/pos_past_order_summary.js", + "selling/page/point_of_sale/pos_controller.js" ] } diff --git a/erpnext/selling/page/point_of_sale/onscan.js b/erpnext/selling/page/point_of_sale/onscan.js deleted file mode 100644 index 428dc75cf8..0000000000 --- a/erpnext/selling/page/point_of_sale/onscan.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t()):e.onScan=t()}(this,function(){var d={attachTo:function(e,t){if(void 0!==e.scannerDetectionData)throw new Error("onScan.js is already initialized for DOM element "+e);var n={onScan:function(e,t){},onScanError:function(e){},onKeyProcess:function(e,t){},onKeyDetect:function(e,t){},onPaste:function(e,t){},keyCodeMapper:function(e){return d.decodeKeyEvent(e)},onScanButtonLongPress:function(){},scanButtonKeyCode:!1,scanButtonLongPressTime:500,timeBeforeScanTest:100,avgTimeByChar:30,minLength:6,suffixKeyCodes:[9,13],prefixKeyCodes:[],ignoreIfFocusOn:!1,stopPropagation:!1,preventDefault:!1,captureEvents:!1,reactToKeydown:!0,reactToPaste:!1,singleScanQty:1};return t=this._mergeOptions(n,t),e.scannerDetectionData={options:t,vars:{firstCharTime:0,lastCharTime:0,accumulatedString:"",testTimer:!1,longPressTimeStart:0,longPressed:!1}},!0===t.reactToPaste&&e.addEventListener("paste",this._handlePaste,t.captureEvents),!1!==t.scanButtonKeyCode&&e.addEventListener("keyup",this._handleKeyUp,t.captureEvents),!0!==t.reactToKeydown&&!1===t.scanButtonKeyCode||e.addEventListener("keydown",this._handleKeyDown,t.captureEvents),this},detachFrom:function(e){e.scannerDetectionData.options.reactToPaste&&e.removeEventListener("paste",this._handlePaste),!1!==e.scannerDetectionData.options.scanButtonKeyCode&&e.removeEventListener("keyup",this._handleKeyUp),e.removeEventListener("keydown",this._handleKeyDown),e.scannerDetectionData=void 0},getOptions:function(e){return e.scannerDetectionData.options},setOptions:function(e,t){switch(e.scannerDetectionData.options.reactToPaste){case!0:!1===t.reactToPaste&&e.removeEventListener("paste",this._handlePaste);break;case!1:!0===t.reactToPaste&&e.addEventListener("paste",this._handlePaste)}switch(e.scannerDetectionData.options.scanButtonKeyCode){case!1:!1!==t.scanButtonKeyCode&&e.addEventListener("keyup",this._handleKeyUp);break;default:!1===t.scanButtonKeyCode&&e.removeEventListener("keyup",this._handleKeyUp)}return e.scannerDetectionData.options=this._mergeOptions(e.scannerDetectionData.options,t),this._reinitialize(e),this},decodeKeyEvent:function(e){var t=this._getNormalizedKeyNum(e);switch(!0){case 48<=t&&t<=90:case 106<=t&&t<=111:if(void 0!==e.key&&""!==e.key)return e.key;var n=String.fromCharCode(t);switch(e.shiftKey){case!1:n=n.toLowerCase();break;case!0:n=n.toUpperCase()}return n;case 96<=t&&t<=105:return t-96}return""},simulate:function(e,t){return this._reinitialize(e),Array.isArray(t)?t.forEach(function(e){var t={};"object"!=typeof e&&"function"!=typeof e||null===e?t.keyCode=parseInt(e):t=e;var n=new KeyboardEvent("keydown",t);document.dispatchEvent(n)}):this._validateScanCode(e,t),this},_reinitialize:function(e){var t=e.scannerDetectionData.vars;t.firstCharTime=0,t.lastCharTime=0,t.accumulatedString=""},_isFocusOnIgnoredElement:function(e){var t=e.scannerDetectionData.options.ignoreIfFocusOn;if(!t)return!1;var n=document.activeElement;if(Array.isArray(t)){for(var a=0;at.length*i.avgTimeByChar:c={message:"Receieved code was not entered in time"};break;default:return i.onScan.call(e,t,o),n=new CustomEvent("scan",{detail:{scanCode:t,qty:o}}),e.dispatchEvent(n),d._reinitialize(e),!0}return c.scanCode=t,c.scanDuration=s-r,c.avgTimeByChar=i.avgTimeByChar,c.minLength=i.minLength,i.onScanError.call(e,c),n=new CustomEvent("scanError",{detail:c}),e.dispatchEvent(n),d._reinitialize(e),!1},_mergeOptions:function(e,t){var n,a={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(a[n]=e[n]);for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(a[n]=t[n]);return a},_getNormalizedKeyNum:function(e){return e.which||e.keyCode},_handleKeyDown:function(e){var t=d._getNormalizedKeyNum(e),n=this.scannerDetectionData.options,a=this.scannerDetectionData.vars,i=!1;if(!1!==n.onKeyDetect.call(this,t,e)&&!d._isFocusOnIgnoredElement(this))if(!1===n.scanButtonKeyCode||t!=n.scanButtonKeyCode){switch(!0){case a.firstCharTime&&-1!==n.suffixKeyCodes.indexOf(t):e.preventDefault(),e.stopImmediatePropagation(),i=!0;break;case!a.firstCharTime&&-1!==n.prefixKeyCodes.indexOf(t):e.preventDefault(),e.stopImmediatePropagation(),i=!1;break;default:var o=n.keyCodeMapper.call(this,e);if(null===o)return;a.accumulatedString+=o,n.preventDefault&&e.preventDefault(),n.stopPropagation&&e.stopImmediatePropagation(),i=!1}a.firstCharTime||(a.firstCharTime=Date.now()),a.lastCharTime=Date.now(),a.testTimer&&clearTimeout(a.testTimer),i?(d._validateScanCode(this,a.accumulatedString),a.testTimer=!1):a.testTimer=setTimeout(d._validateScanCode,n.timeBeforeScanTest,this,a.accumulatedString),n.onKeyProcess.call(this,o,e)}else a.longPressed||(a.longPressTimer=setTimeout(n.onScanButtonLongPress,n.scanButtonLongPressTime,this),a.longPressed=!0)},_handlePaste:function(e){if(!d._isFocusOnIgnoredElement(this)){e.preventDefault(),oOptions.stopPropagation&&e.stopImmediatePropagation();var t=(event.clipboardData||window.clipboardData).getData("text");this.scannerDetectionData.options.onPaste.call(this,t,event);var n=this.scannerDetectionData.vars;n.firstCharTime=0,n.lastCharTime=0,d._validateScanCode(this,t)}},_handleKeyUp:function(e){d._isFocusOnIgnoredElement(this)||d._getNormalizedKeyNum(e)==this.scannerDetectionData.options.scanButtonKeyCode&&(clearTimeout(this.scannerDetectionData.vars.longPressTimer),this.scannerDetectionData.vars.longPressed=!1)},isScanInProgressFor:function(e){return 0 { + primary_action: async function({ company, pos_profile, balance_details }) { if (!balance_details.length) { frappe.show_alert({ message: __("Please add Mode of payments and opening balance details."), @@ -98,7 +90,7 @@ erpnext.PointOfSale.Controller = class { } const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher"; const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true }); - !res.exc && this.prepare_app_defaults(res.message); + !res.exc && me.prepare_app_defaults(res.message); dialog.hide(); }, primary_action_label: __('Submit') diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 3588e308c8..0d0e36cb3e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -1,3 +1,5 @@ +import onScan from 'onscan.js'; + erpnext.PointOfSale.ItemSelector = class { constructor({ frm, wrapper, events, pos_profile }) { this.wrapper = wrapper; @@ -46,7 +48,8 @@ erpnext.PointOfSale.ItemSelector = class { } get_items({start = 0, page_length = 40, search_value=''}) { - const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list; + const doc = this.events.get_frm().doc; + const price_list = (doc && doc.selling_price_list) || this.price_list; let { item_group, pos_profile } = this; !item_group && (item_group = this.parent_item_group); @@ -104,6 +107,7 @@ erpnext.PointOfSale.ItemSelector = class { make_search_bar() { const me = this; + const doc = me.events.get_frm().doc; this.$component.find('.search-field').html(''); this.$component.find('.item-group-field').html(''); @@ -131,7 +135,7 @@ erpnext.PointOfSale.ItemSelector = class { return { query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query', filters: { - pos_profile: me.events.get_frm().doc?.pos_profile + pos_profile: doc ? doc.pos_profile : '' } } }, @@ -145,6 +149,7 @@ erpnext.PointOfSale.ItemSelector = class { bind_events() { const me = this; + window.onScan = onScan; onScan.attachTo(document, { onScan: (sScancode) => { if (this.search_field && this.$component.is(':visible')) { diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 6476b4925a..365c27b12f 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -1,5 +1,3 @@ -{% include "erpnext/selling/page/point_of_sale/pos_number_pad.js" %} - erpnext.PointOfSale.Payment = class { constructor({ events, wrapper }) { this.wrapper = wrapper; @@ -153,13 +151,12 @@ erpnext.PointOfSale.Payment = class { me.$payment_modes.find(`.${mode}-amount`).css('display', 'none'); me.$payment_modes.find(`.${mode}-name`).css('display', 'inline'); - me.selected_mode = me[`${mode}_control`]; const doc = me.events.get_frm().doc; - me.selected_mode?.$input?.get(0).focus(); - const current_value = me.selected_mode?.get_value() - !current_value && doc.grand_total > doc.paid_amount ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : ''; - - me.numpad_value = ''; + me.selected_mode = me[`${mode}_control`]; + me.selected_mode && me.selected_mode.$input.get(0).focus(); + const current_value = me.selected_mode ? me.selected_mode.get_value() : undefined; + !current_value && doc.grand_total > doc.paid_amount && me.selected_mode ? + me.selected_mode.set_value(doc.grand_total - doc.paid_amount) : ''; } }) diff --git a/package.json b/package.json index 1b2dc9efcf..d12661b5cc 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "snyk": "^1.290.1" }, "dependencies": { + "onscan.js": "^1.5.2" }, "scripts": { "snyk-protect": "snyk protect", diff --git a/yarn.lock b/yarn.lock index 97a063597d..e5a2da1e40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1217,6 +1217,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onscan.js@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/onscan.js/-/onscan.js-1.5.2.tgz#14ed636e5f4c3f0a78bacbf9a505dad3140ee341" + integrity sha512-9oGYy2gXYRjvXO9GYqqVca0VuCTAmWhbmX3egBSBP13rXiMNb+dKPJzKFEeECGqPBpf0m40Zoo+GUQ7eCackdw== + opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" From c1afc95e3cdcf59a8c91eaa8d303e0698f7a19d4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:56:54 +0530 Subject: [PATCH 138/283] chore: remove unused code --- erpnext/public/build.json | 10 - .../public/js/payment/payment_details.html | 11 - erpnext/public/js/payment/payments.js | 239 ------------- erpnext/public/js/payment/pos_payment.html | 42 --- erpnext/public/js/pos/clusterize.js | 330 ------------------ erpnext/public/js/pos/customer_toolbar.html | 16 - erpnext/public/js/pos/pos.html | 136 -------- erpnext/public/js/pos/pos_bill_item.html | 34 -- erpnext/public/js/pos/pos_bill_item_new.html | 9 - erpnext/public/js/pos/pos_invoice_list.html | 9 - erpnext/public/js/pos/pos_item.html | 32 -- erpnext/public/js/pos/pos_selected_item.html | 22 -- erpnext/public/js/pos/pos_tax_row.html | 4 - 13 files changed, 894 deletions(-) delete mode 100644 erpnext/public/js/payment/payment_details.html delete mode 100644 erpnext/public/js/payment/payments.js delete mode 100644 erpnext/public/js/payment/pos_payment.html delete mode 100644 erpnext/public/js/pos/clusterize.js delete mode 100644 erpnext/public/js/pos/customer_toolbar.html delete mode 100644 erpnext/public/js/pos/pos.html delete mode 100644 erpnext/public/js/pos/pos_bill_item.html delete mode 100644 erpnext/public/js/pos/pos_bill_item_new.html delete mode 100644 erpnext/public/js/pos/pos_invoice_list.html delete mode 100755 erpnext/public/js/pos/pos_item.html delete mode 100644 erpnext/public/js/pos/pos_selected_item.html delete mode 100644 erpnext/public/js/pos/pos_tax_row.html diff --git a/erpnext/public/build.json b/erpnext/public/build.json index b12045c4c8..ec15b88939 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -28,16 +28,6 @@ "public/js/payment/payments.js", "public/js/controllers/taxes_and_totals.js", "public/js/controllers/transaction.js", - "public/js/pos/pos.html", - "public/js/pos/pos_bill_item.html", - "public/js/pos/pos_bill_item_new.html", - "public/js/pos/pos_selected_item.html", - "public/js/pos/pos_item.html", - "public/js/pos/pos_tax_row.html", - "public/js/pos/customer_toolbar.html", - "public/js/pos/pos_invoice_list.html", - "public/js/payment/pos_payment.html", - "public/js/payment/payment_details.html", "public/js/templates/item_selector.html", "public/js/templates/employees_to_mark_attendance.html", "public/js/utils/item_selector.js", diff --git a/erpnext/public/js/payment/payment_details.html b/erpnext/public/js/payment/payment_details.html deleted file mode 100644 index 3e6394483e..0000000000 --- a/erpnext/public/js/payment/payment_details.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
{{mode_of_payment}}
-
-
- - - - -
-
-
\ No newline at end of file diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js deleted file mode 100644 index 0d656bc1fb..0000000000 --- a/erpnext/public/js/payment/payments.js +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -erpnext.payments = erpnext.stock.StockController.extend({ - make_payment: function() { - var me = this; - - this.dialog = new frappe.ui.Dialog({ - title: 'Payment' - }); - - this.dialog.show(); - this.$body = this.dialog.body; - this.set_payment_primary_action(); - this.make_keyboard(); - this.select_text() - }, - - select_text: function(){ - var me = this; - $(this.$body).find('.form-control').click(function(){ - $(this).select(); - }) - }, - - set_payment_primary_action: function(){ - var me = this; - - this.dialog.set_primary_action(__("Submit"), function() { - // Allow no ZERO payment - $.each(me.frm.doc.payments, function (index, data) { - if (data.amount != 0) { - me.dialog.hide(); - me.submit_invoice(); - return; - } - }); - }) - }, - - make_keyboard: function(){ - var me = this; - $(this.$body).empty(); - $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) - this.show_payment_details(); - this.bind_keyboard_event() - this.clear_amount() - }, - - make_multimode_payment: function(){ - var me = this; - - if(this.frm.doc.change_amount > 0){ - me.payment_val = me.doc.outstanding_amount - } - - this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments"); - this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value(); - this.payments.amount = flt(this.payment_val); - }, - - show_payment_details: function(){ - var me = this; - var multimode_payments = $(this.$body).find('.multimode-payments').empty(); - if(this.frm.doc.payments.length){ - $.each(this.frm.doc.payments, function(index, data){ - $(frappe.render_template('payment_details', { - mode_of_payment: data.mode_of_payment, - amount: data.amount, - idx: data.idx, - currency: me.frm.doc.currency, - type: data.type - })).appendTo(multimode_payments) - - if (data.type == 'Cash' && data.amount == me.frm.doc.paid_amount) { - me.idx = data.idx; - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); - me.highlight_selected_row(); - me.bind_amount_change_event(); - } - }) - }else{ - $("

No payment mode selected in pos profile

").appendTo(multimode_payments) - } - }, - - set_outstanding_amount: function(){ - this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx})); - this.highlight_selected_row() - this.payment_val = 0.0 - if(this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0){ - //When user first time click on row - this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount")) - this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency)); - this.update_payment_amount() - }else if(flt(this.selected_mode.val()) > 0){ - //If user click on existing row which has value - this.payment_val = flt(this.selected_mode.val()); - } - this.selected_mode.select() - this.bind_amount_change_event(); - }, - - bind_keyboard_event: function(){ - var me = this; - this.payment_val = ''; - this.bind_form_control_event(); - this.bind_numeric_keys_event(); - }, - - bind_form_control_event: function(){ - var me = this; - $(this.$body).find('.pos-payment-row').click(function(){ - me.idx = $(this).attr("idx"); - me.set_outstanding_amount() - }) - - $(this.$body).find('.form-control').click(function(){ - me.idx = $(this).attr("idx"); - me.set_outstanding_amount(); - me.update_paid_amount(true); - }) - - $(this.$body).find('.write_off_amount').change(function(){ - me.write_off_amount(flt($(this).val()), precision("write_off_amount")); - }) - - $(this.$body).find('.change_amount').change(function(){ - me.change_amount(flt($(this).val()), precision("change_amount")); - }) - }, - - highlight_selected_row: function(){ - var me = this; - var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx})); - $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode') - selected_row.addClass('selected-payment-mode') - $(this.$body).find('.amount').attr('disabled', true); - this.selected_mode.attr('disabled', false); - }, - - bind_numeric_keys_event: function(){ - var me = this; - $(this.$body).find('.pos-keyboard-key').click(function(){ - me.payment_val += $(this).text(); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) - me.idx = me.selected_mode.attr("idx") - me.update_paid_amount() - }) - - $(this.$body).find('.delete-btn').click(function(){ - me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); - me.idx = me.selected_mode.attr("idx") - me.update_paid_amount(); - }) - - }, - - bind_amount_change_event: function(){ - var me = this; - this.selected_mode.change(function(){ - me.payment_val = flt($(this).val()) || 0.0; - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) - me.idx = me.selected_mode.attr("idx") - me.update_payment_amount() - }) - }, - - clear_amount: function() { - var me = this; - $(this.$body).find('.clr').click(function(e){ - e.stopPropagation(); - me.idx = $(this).attr("idx"); - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); - me.payment_val = 0.0; - me.selected_mode.val(0.0); - me.highlight_selected_row(); - me.update_payment_amount(); - }) - }, - - write_off_amount: function(write_off_amount) { - var me = this; - - this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount")); - this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, - precision("base_write_off_amount")); - this.calculate_outstanding_amount(false) - this.show_amounts() - }, - - change_amount: function(change_amount) { - var me = this; - - this.frm.doc.change_amount = flt(change_amount, precision("change_amount")); - this.calculate_write_off_amount() - this.show_amounts() - }, - - update_paid_amount: function(update_write_off) { - var me = this; - if(in_list(['change_amount', 'write_off_amount'], this.idx)){ - var value = me.selected_mode.val(); - if(me.idx == 'change_amount'){ - me.change_amount(value) - } else{ - if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { - value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx)); - } - me.write_off_amount(value) - } - }else{ - this.update_payment_amount() - } - }, - - update_payment_amount: function(){ - var me = this; - - $.each(this.frm.doc.payments, function(index, data){ - if(cint(me.idx) == cint(data.idx)){ - data.amount = flt(me.selected_mode.val(), 2) - } - }) - - this.calculate_outstanding_amount(false); - this.show_amounts(); - }, - - show_amounts: function(){ - var me = this; - $(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); - $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); - $(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)) - $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency)) - this.update_invoice(); - } -}) \ No newline at end of file diff --git a/erpnext/public/js/payment/pos_payment.html b/erpnext/public/js/payment/pos_payment.html deleted file mode 100644 index cb6971b46b..0000000000 --- a/erpnext/public/js/payment/pos_payment.html +++ /dev/null @@ -1,42 +0,0 @@ -
-
-

{{ __("Total Amount") }}: {%= format_currency(grand_total, currency) %}

-
-
-
-

{{ __("Paid") }}

-
-
-

{{ __("Outstanding") }}

{%= format_currency(outstanding_amount, currency) %}

-
-
-

{{ __("Change") }} -

-
-
-

{{ __("Write off") }} -

-
-
-
-
-
-
-
-
-
- {% for(var i=0; i<3; i++) { %} -
- {% for(var j=i*3; j<(i+1)*3; j++) { %} - - {% } %} -
- {% } %} -
- - - -
-
-
-
diff --git a/erpnext/public/js/pos/clusterize.js b/erpnext/public/js/pos/clusterize.js deleted file mode 100644 index 075c9ca4ae..0000000000 --- a/erpnext/public/js/pos/clusterize.js +++ /dev/null @@ -1,330 +0,0 @@ -/* eslint-disable */ -/*! Clusterize.js - v0.17.6 - 2017-03-05 -* http://NeXTs.github.com/Clusterize.js/ -* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */ - -;(function(name, definition) { - if (typeof module != 'undefined') module.exports = definition(); - else if (typeof define == 'function' && typeof define.amd == 'object') define(definition); - else this[name] = definition(); -}('Clusterize', function() { - "use strict" - - // detect ie9 and lower - // https://gist.github.com/padolsey/527683#comment-786682 - var ie = (function(){ - for( var v = 3, - el = document.createElement('b'), - all = el.all || []; - el.innerHTML = '', - all[0]; - ){} - return v > 4 ? v : document.documentMode; - }()), - is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1; - var Clusterize = function(data) { - if( ! (this instanceof Clusterize)) - return new Clusterize(data); - var self = this; - - var defaults = { - rows_in_block: 50, - blocks_in_cluster: 4, - tag: null, - show_no_data_row: true, - no_data_class: 'clusterize-no-data', - no_data_text: 'No data', - keep_parity: true, - callbacks: {} - } - - // public parameters - self.options = {}; - var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks']; - for(var i = 0, option; option = options[i]; i++) { - self.options[option] = typeof data[option] != 'undefined' && data[option] != null - ? data[option] - : defaults[option]; - } - - var elems = ['scroll', 'content']; - for(var i = 0, elem; elem = elems[i]; i++) { - self[elem + '_elem'] = data[elem + 'Id'] - ? document.getElementById(data[elem + 'Id']) - : data[elem + 'Elem']; - if( ! self[elem + '_elem']) - throw new Error("Error! Could not find " + elem + " element"); - } - - // tabindex forces the browser to keep focus on the scrolling list, fixes #11 - if( ! self.content_elem.hasAttribute('tabindex')) - self.content_elem.setAttribute('tabindex', 0); - - // private parameters - var rows = isArray(data.rows) - ? data.rows - : self.fetchMarkup(), - cache = {}, - scroll_top = self.scroll_elem.scrollTop; - - // append initial data - self.insertToDOM(rows, cache); - - // restore the scroll position - self.scroll_elem.scrollTop = scroll_top; - - // adding scroll handler - var last_cluster = false, - scroll_debounce = 0, - pointer_events_set = false, - scrollEv = function() { - // fixes scrolling issue on Mac #3 - if (is_mac) { - if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none'; - pointer_events_set = true; - clearTimeout(scroll_debounce); - scroll_debounce = setTimeout(function () { - self.content_elem.style.pointerEvents = 'auto'; - pointer_events_set = false; - }, 50); - } - if (last_cluster != (last_cluster = self.getClusterNum())) - self.insertToDOM(rows, cache); - if (self.options.callbacks.scrollingProgress) - self.options.callbacks.scrollingProgress(self.getScrollProgress()); - }, - resize_debounce = 0, - resizeEv = function() { - clearTimeout(resize_debounce); - resize_debounce = setTimeout(self.refresh, 100); - } - on('scroll', self.scroll_elem, scrollEv); - on('resize', window, resizeEv); - - // public methods - self.destroy = function(clean) { - off('scroll', self.scroll_elem, scrollEv); - off('resize', window, resizeEv); - self.html((clean ? self.generateEmptyRow() : rows).join('')); - } - self.refresh = function(force) { - if(self.getRowsHeight(rows) || force) self.update(rows); - } - self.update = function(new_rows) { - rows = isArray(new_rows) - ? new_rows - : []; - var scroll_top = self.scroll_elem.scrollTop; - // fixes #39 - if(rows.length * self.options.item_height < scroll_top) { - self.scroll_elem.scrollTop = 0; - last_cluster = 0; - } - self.insertToDOM(rows, cache); - self.scroll_elem.scrollTop = scroll_top; - } - self.clear = function() { - self.update([]); - } - self.getRowsAmount = function() { - return rows.length; - } - self.getScrollProgress = function() { - return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0; - } - - var add = function(where, _new_rows) { - var new_rows = isArray(_new_rows) - ? _new_rows - : []; - if( ! new_rows.length) return; - rows = where == 'append' - ? rows.concat(new_rows) - : new_rows.concat(rows); - self.insertToDOM(rows, cache); - } - self.append = function(rows) { - add('append', rows); - } - self.prepend = function(rows) { - add('prepend', rows); - } - } - - Clusterize.prototype = { - constructor: Clusterize, - // fetch existing markup - fetchMarkup: function() { - var rows = [], rows_nodes = this.getChildNodes(this.content_elem); - while (rows_nodes.length) { - rows.push(rows_nodes.shift().outerHTML); - } - return rows; - }, - // get tag name, content tag name, tag height, calc cluster height - exploreEnvironment: function(rows, cache) { - var opts = this.options; - opts.content_tag = this.content_elem.tagName.toLowerCase(); - if( ! rows.length) return; - if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase(); - if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]); - if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase(); - this.getRowsHeight(rows); - }, - getRowsHeight: function(rows) { - var opts = this.options, - prev_item_height = opts.item_height; - opts.cluster_height = 0; - if( ! rows.length) return; - var nodes = this.content_elem.children; - var node = nodes[Math.floor(nodes.length / 2)]; - opts.item_height = node.offsetHeight; - // consider table's border-spacing - if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse') - opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0; - // consider margins (and margins collapsing) - if(opts.tag != 'tr') { - var marginTop = parseInt(getStyle('marginTop', node), 10) || 0; - var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0; - opts.item_height += Math.max(marginTop, marginBottom); - } - opts.block_height = opts.item_height * opts.rows_in_block; - opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block; - opts.cluster_height = opts.blocks_in_cluster * opts.block_height; - return prev_item_height != opts.item_height; - }, - // get current cluster number - getClusterNum: function () { - this.options.scroll_top = this.scroll_elem.scrollTop; - return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0; - }, - // generate empty row if no data provided - generateEmptyRow: function() { - var opts = this.options; - if( ! opts.tag || ! opts.show_no_data_row) return []; - var empty_row = document.createElement(opts.tag), - no_data_content = document.createTextNode(opts.no_data_text), td; - empty_row.className = opts.no_data_class; - if(opts.tag == 'tr') { - td = document.createElement('td'); - // fixes #53 - td.colSpan = 100; - td.appendChild(no_data_content); - } - empty_row.appendChild(td || no_data_content); - return [empty_row.outerHTML]; - }, - // generate cluster for current scroll position - generate: function (rows, cluster_num) { - var opts = this.options, - rows_len = rows.length; - if (rows_len < opts.rows_in_block) { - return { - top_offset: 0, - bottom_offset: 0, - rows_above: 0, - rows: rows_len ? rows : this.generateEmptyRow() - } - } - var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0), - items_end = items_start + opts.rows_in_cluster, - top_offset = Math.max(items_start * opts.item_height, 0), - bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0), - this_cluster_rows = [], - rows_above = items_start; - if(top_offset < 1) { - rows_above++; - } - for (var i = items_start; i < items_end; i++) { - rows[i] && this_cluster_rows.push(rows[i]); - } - return { - top_offset: top_offset, - bottom_offset: bottom_offset, - rows_above: rows_above, - rows: this_cluster_rows - } - }, - renderExtraTag: function(class_name, height) { - var tag = document.createElement(this.options.tag), - clusterize_prefix = 'clusterize-'; - tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' '); - height && (tag.style.height = height + 'px'); - return tag.outerHTML; - }, - // if necessary verify data changed and insert to DOM - insertToDOM: function(rows, cache) { - // explore row's height - if( ! this.options.cluster_height) { - this.exploreEnvironment(rows, cache); - } - var data = this.generate(rows, this.getClusterNum()), - this_cluster_rows = data.rows.join(''), - this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache), - top_offset_changed = this.checkChanges('top', data.top_offset, cache), - only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache), - callbacks = this.options.callbacks, - layout = []; - - if(this_cluster_content_changed || top_offset_changed) { - if(data.top_offset) { - this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity')); - layout.push(this.renderExtraTag('top-space', data.top_offset)); - } - layout.push(this_cluster_rows); - data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset)); - callbacks.clusterWillChange && callbacks.clusterWillChange(); - this.html(layout.join('')); - this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above); - callbacks.clusterChanged && callbacks.clusterChanged(); - } else if(only_bottom_offset_changed) { - this.content_elem.lastChild.style.height = data.bottom_offset + 'px'; - } - }, - // unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround - html: function(data) { - var content_elem = this.content_elem; - if(ie && ie <= 9 && this.options.tag == 'tr') { - var div = document.createElement('div'), last; - div.innerHTML = '' + data + '
'; - while((last = content_elem.lastChild)) { - content_elem.removeChild(last); - } - var rows_nodes = this.getChildNodes(div.firstChild.firstChild); - while (rows_nodes.length) { - content_elem.appendChild(rows_nodes.shift()); - } - } else { - content_elem.innerHTML = data; - } - }, - getChildNodes: function(tag) { - var child_nodes = tag.children, nodes = []; - for (var i = 0, ii = child_nodes.length; i < ii; i++) { - nodes.push(child_nodes[i]); - } - return nodes; - }, - checkChanges: function(type, value, cache) { - var changed = value != cache[type]; - cache[type] = value; - return changed; - } - } - - // support functions - function on(evt, element, fnc) { - return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc); - } - function off(evt, element, fnc) { - return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc); - } - function isArray(arr) { - return Object.prototype.toString.call(arr) === '[object Array]'; - } - function getStyle(prop, elem) { - return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop]; - } - - return Clusterize; -})); \ No newline at end of file diff --git a/erpnext/public/js/pos/customer_toolbar.html b/erpnext/public/js/pos/customer_toolbar.html deleted file mode 100644 index 3ba5ccbc67..0000000000 --- a/erpnext/public/js/pos/customer_toolbar.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
- - - - - -
- - {% if (allow_delete) { %} - {% } %} -
\ No newline at end of file diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html deleted file mode 100644 index 89e2940c89..0000000000 --- a/erpnext/public/js/pos/pos.html +++ /dev/null @@ -1,136 +0,0 @@ -
-
-
-
{{ __("Item Cart") }}
-
-
-
- - - {{ __("Item Name")}} - - {{ __("Quantity") }} - {{ __("Discount") }} - {{ __("Rate") }} -
-
-
- - -

{{ __("Tap items to add them here") }}

-
-
-
-
-
-
-
-
-
-
-
{%= __("Net Total") %}
-
-
-
-
-
{%= __("Taxes") %}
-
-
-
- {% if(allow_user_to_edit_discount) { %} -
-
-
{%= __("Discount") %}
-
-
- % - -
-
- {%= get_currency_symbol(currency) %} - -
-
-
- {% } %} -
-
- - - -
-
{%= __("Grand Total") %}
-
-
-
-
- - - -
-
{%= __("Qty Total") %}
-
-
-
-
-
- -
- -
-
-
-
{{ __("Customers in Queue") }}
-
-
{{ __("Customer") }}
-
{{ __("Status") }}
-
{{ __("Amount") }}
-
{{ __("Grand Total") }}
-
-
-
- - -

{{ __("No Customers yet!") }}

-
-
-
-
-
-
{{ __("Stock Items") }}
-
- -
- -
-
- -
-
-
- -
-
-
-
diff --git a/erpnext/public/js/pos/pos_bill_item.html b/erpnext/public/js/pos/pos_bill_item.html deleted file mode 100644 index 21868a6cae..0000000000 --- a/erpnext/public/js/pos/pos_bill_item.html +++ /dev/null @@ -1,34 +0,0 @@ -
-
{%= item_code || "" %}{%= __(item_name) || "" %}
-
-
-
-
-
- -
- {% if(actual_qty != null) { %} -
- {%= __("In Stock: ") %} {%= actual_qty || 0.0 %} -
- {% } %} -
-
-
-
-
-
- -
-
-
-
- {% if(enabled) { %} - - {% } else { %} -
{%= format_currency(rate) %}
- {% } %} -
-

{%= amount %}

-
-
diff --git a/erpnext/public/js/pos/pos_bill_item_new.html b/erpnext/public/js/pos/pos_bill_item_new.html deleted file mode 100644 index cb626cefce..0000000000 --- a/erpnext/public/js/pos/pos_bill_item_new.html +++ /dev/null @@ -1,9 +0,0 @@ -
- -
{%= qty %}
-
{%= discount_percentage %}
-
{%= format_currency(rate) %}
-
diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html deleted file mode 100644 index 13aa52055a..0000000000 --- a/erpnext/public/js/pos/pos_invoice_list.html +++ /dev/null @@ -1,9 +0,0 @@ -
- -
{{ data.status }}
-
{%= paid_amount %}
-
{%= grand_total %}
-
diff --git a/erpnext/public/js/pos/pos_item.html b/erpnext/public/js/pos/pos_item.html deleted file mode 100755 index 52f3cf698a..0000000000 --- a/erpnext/public/js/pos/pos_item.html +++ /dev/null @@ -1,32 +0,0 @@ - \ No newline at end of file diff --git a/erpnext/public/js/pos/pos_selected_item.html b/erpnext/public/js/pos/pos_selected_item.html deleted file mode 100644 index 03c73411a4..0000000000 --- a/erpnext/public/js/pos/pos_selected_item.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
{{ __("Quantity") }}:
- -
-
-
{{ __("Price List Rate") }}:
- -
-
-
{{ __("Discount") }}: %
- -
-
-
{{ __("Price") }}:
- -
-
-
{{ __("Amount") }}:
- -
-
\ No newline at end of file diff --git a/erpnext/public/js/pos/pos_tax_row.html b/erpnext/public/js/pos/pos_tax_row.html deleted file mode 100644 index 3752a89bbd..0000000000 --- a/erpnext/public/js/pos/pos_tax_row.html +++ /dev/null @@ -1,4 +0,0 @@ -
-
{%= description %}
-
{%= tax_amount %}
-
From 6aa6ec1832d9c4caf00e4f113845b47f80dd92bb Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 24 Nov 2020 08:01:19 +0530 Subject: [PATCH 139/283] fix: clear error message when approval not available (#23971) --- .../department_approver/department_approver.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py index 9b2de0e1cb..d337959d53 100644 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ b/erpnext/hr/doctype/department_approver/department_approver.py @@ -20,7 +20,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): approvers = [] department_details = {} department_list = [] - employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True) + employee = frappe.get_value("Employee", filters.get("employee"), ["employee_name","department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True) employee_department = filters.get("department") or employee.department if employee_department: @@ -59,11 +59,9 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True) if len(approvers) == 0: - frappe.throw(_("Please set {0} for the Employee or for Department: {1}"). - format( - field_name, frappe.bold(employee_department), - frappe.bold(employee.name) - ), - title=_(field_name + " Missing")) + error_msg = _("Please set {0} for the Employee: {1}").format(field_name, frappe.bold(employee.employee_name)) + if department_list: + error_msg += _(" or for Department: {0}").format(frappe.bold(employee_department)) + frappe.throw(error_msg, title=_(field_name + " Missing")) return set(tuple(approver) for approver in approvers) From d07447aa5fbeed93e72e882230e6b571a47b6611 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 24 Nov 2020 08:09:17 +0530 Subject: [PATCH 140/283] fix: Validation for duplicate Tax Category (#23978) * fix: Validation for duplicate Tax Category * Update utils.py Co-authored-by: Nabin Hait --- erpnext/hooks.py | 3 +++ erpnext/regional/india/utils.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b4c57d7c91..741176f33f 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -237,6 +237,9 @@ doc_events = { "Website Settings": { "validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products" }, + "Tax Category": { + "validate": "erpnext.regional.india.utils.validate_tax_category" + }, "Sales Invoice": { "on_submit": [ "erpnext.regional.create_transaction_log", diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index fc38ed0972..62487ba2aa 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -51,6 +51,13 @@ def validate_gstin_for_india(doc, method): frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.") .format(doc.gst_state_number)) +def validate_tax_category(doc, method): + if doc.get('gst_state') and frappe.db.get_value('Tax category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}): + if doc.is_inter_state: + frappe.throw(_("Inter State tax category for GST State {0} already exists").format(doc.gst_state)) + else: + frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state)) + def update_gst_category(doc, method): for link in doc.links: if link.link_doctype in ['Customer', 'Supplier']: @@ -85,8 +92,7 @@ def validate_gstin_check_digit(gstin, label='GSTIN'): total += digit factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: - frappe.throw(_("""Invalid {0}! The check digit validation has failed. - Please ensure you've typed the {0} correctly.""").format(label)) + frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label)) def get_itemised_tax_breakup_header(item_doctype, tax_accounts): if frappe.get_meta(item_doctype).has_field('gst_hsn_code'): @@ -515,7 +521,7 @@ def get_address_details(data, doc, company_address, billing_address): data.transType = 1 data.actualToStateCode = data.toStateCode shipping_address = billing_address - + if doc.gst_category == 'SEZ': data.toStateCode = 99 @@ -754,4 +760,4 @@ def make_regional_gl_entries(gl_entries, doc): }, account_currency, item=tax) ) - return gl_entries \ No newline at end of file + return gl_entries From e09037ed2c1e5cf634fbd993e1d135cae84b0673 Mon Sep 17 00:00:00 2001 From: Krushnal Patel Date: Tue, 24 Nov 2020 12:53:30 +0530 Subject: [PATCH 141/283] docs: README build status badge (#23933) * fixed build status badge * changed build branch from `master` to `develop` * updated build status badge url * Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f6a52142b..15782a2e0c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

ERP made simple

-[![Build Status](https://travis-ci.com/frappe/erpnext.svg)](https://travis-ci.com/frappe/erpnext) +[![Build Status](https://api.travis-ci.com/frappe/erpnext.svg?branch=develop)](https://travis-ci.com/frappe/erpnext) [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext) [![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop) From 927106f5528bfebe8d43ae24ac627ae9965720d5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 24 Nov 2020 15:02:52 +0530 Subject: [PATCH 142/283] fix: maintain stock can't be changed it there is product bundle --- erpnext/stock/doctype/item/item.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3b62c38b86..be845d9d9d 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -977,15 +977,20 @@ class Item(WebsiteGenerator): # For "Is Stock Item", following doctypes is important # because reserved_qty, ordered_qty and requested_qty updated from these doctypes if field == "is_stock_item": - linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] + linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item", "Product Bundle"] for doctype in linked_doctypes: + filters={"item_code": self.name, "docstatus": 1} + + if doctype == "Product Bundle": + filters={"new_item_code": self.name} + if doctype in ("Purchase Invoice Item", "Sales Invoice Item",): # If Invoice has Stock impact, only then consider it. if self.stock_ledger_created(): return True - elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}): + elif frappe.db.get_value(doctype, filters): return True def validate_auto_reorder_enabled_in_stock_settings(self): From 43a830f3f593f463f695c7419e1b7961ff96d79d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 24 Nov 2020 15:10:36 +0530 Subject: [PATCH 143/283] fix: Old shopify order syncing date --- .../connectors/shopify_connection.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 8aa7453bd6..efbaa71924 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -149,26 +149,28 @@ def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=Fal si.shopify_order_number = shopify_order.get("name") si.set_posting_time = 1 si.posting_date = posting_date + si.due_date = posting_date si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" si.flags.ignore_mandatory = True set_cost_center(si.items, shopify_settings.cost_center) si.insert(ignore_mandatory=True) si.submit() - make_payament_entry_against_sales_invoice(si, shopify_settings) + make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date) frappe.db.commit() def set_cost_center(items, cost_center): for item in items: item.cost_center = cost_center -def make_payament_entry_against_sales_invoice(doc, shopify_settings): +def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None): from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - payemnt_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) - payemnt_entry.flags.ignore_mandatory = True - payemnt_entry.reference_no = doc.name - payemnt_entry.reference_date = nowdate() - payemnt_entry.insert(ignore_permissions=True) - payemnt_entry.submit() + payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) + payment_entry.flags.ignore_mandatory = True + payment_entry.reference_no = doc.name + payment_entry.posting_date = posting_date or nowdate() + payment_entry.reference_date = posting_date or nowdate() + payment_entry.insert(ignore_permissions=True) + payment_entry.submit() def create_delivery_note(shopify_order, shopify_settings, so): if not cint(shopify_settings.sync_delivery_note): From b67ebc7636187f1e4ee508579a77cbb6a7e223d1 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 24 Nov 2020 15:37:30 +0530 Subject: [PATCH 144/283] fix: job card error handling for operations field --- .../doctype/job_card/job_card.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 4dfa78bf21..d15d81ed93 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -353,17 +353,19 @@ def get_operation_details(work_order, operation): @frappe.whitelist() def get_operations(doctype, txt, searchfield, start, page_len, filters): - if filters.get("work_order"): - args = {"parent": filters.get("work_order")} - if txt: - args["operation"] = ("like", "%{0}%".format(txt)) + if not filters.get("work_order"): + frappe.msgprint(_("Please select a Work Order first.")) + return [] + args = {"parent": filters.get("work_order")} + if txt: + args["operation"] = ("like", "%{0}%".format(txt)) - return frappe.get_all("Work Order Operation", - filters = args, - fields = ["distinct operation as operation"], - limit_start = start, - limit_page_length = page_len, - order_by="idx asc", as_list=1) + return frappe.get_all("Work Order Operation", + filters = args, + fields = ["distinct operation as operation"], + limit_start = start, + limit_page_length = page_len, + order_by="idx asc", as_list=1) @frappe.whitelist() def make_material_request(source_name, target_doc=None): From 7f1d7894c204b3dcadca47453c4556af2a72b86f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 24 Nov 2020 14:04:49 +0530 Subject: [PATCH 145/283] fix: cannot find erpnext.payments --- erpnext/public/js/payment/payments.js | 239 ++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 erpnext/public/js/payment/payments.js diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js new file mode 100644 index 0000000000..0d656bc1fb --- /dev/null +++ b/erpnext/public/js/payment/payments.js @@ -0,0 +1,239 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +erpnext.payments = erpnext.stock.StockController.extend({ + make_payment: function() { + var me = this; + + this.dialog = new frappe.ui.Dialog({ + title: 'Payment' + }); + + this.dialog.show(); + this.$body = this.dialog.body; + this.set_payment_primary_action(); + this.make_keyboard(); + this.select_text() + }, + + select_text: function(){ + var me = this; + $(this.$body).find('.form-control').click(function(){ + $(this).select(); + }) + }, + + set_payment_primary_action: function(){ + var me = this; + + this.dialog.set_primary_action(__("Submit"), function() { + // Allow no ZERO payment + $.each(me.frm.doc.payments, function (index, data) { + if (data.amount != 0) { + me.dialog.hide(); + me.submit_invoice(); + return; + } + }); + }) + }, + + make_keyboard: function(){ + var me = this; + $(this.$body).empty(); + $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) + this.show_payment_details(); + this.bind_keyboard_event() + this.clear_amount() + }, + + make_multimode_payment: function(){ + var me = this; + + if(this.frm.doc.change_amount > 0){ + me.payment_val = me.doc.outstanding_amount + } + + this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments"); + this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value(); + this.payments.amount = flt(this.payment_val); + }, + + show_payment_details: function(){ + var me = this; + var multimode_payments = $(this.$body).find('.multimode-payments').empty(); + if(this.frm.doc.payments.length){ + $.each(this.frm.doc.payments, function(index, data){ + $(frappe.render_template('payment_details', { + mode_of_payment: data.mode_of_payment, + amount: data.amount, + idx: data.idx, + currency: me.frm.doc.currency, + type: data.type + })).appendTo(multimode_payments) + + if (data.type == 'Cash' && data.amount == me.frm.doc.paid_amount) { + me.idx = data.idx; + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); + me.highlight_selected_row(); + me.bind_amount_change_event(); + } + }) + }else{ + $("

No payment mode selected in pos profile

").appendTo(multimode_payments) + } + }, + + set_outstanding_amount: function(){ + this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx})); + this.highlight_selected_row() + this.payment_val = 0.0 + if(this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0){ + //When user first time click on row + this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount")) + this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency)); + this.update_payment_amount() + }else if(flt(this.selected_mode.val()) > 0){ + //If user click on existing row which has value + this.payment_val = flt(this.selected_mode.val()); + } + this.selected_mode.select() + this.bind_amount_change_event(); + }, + + bind_keyboard_event: function(){ + var me = this; + this.payment_val = ''; + this.bind_form_control_event(); + this.bind_numeric_keys_event(); + }, + + bind_form_control_event: function(){ + var me = this; + $(this.$body).find('.pos-payment-row').click(function(){ + me.idx = $(this).attr("idx"); + me.set_outstanding_amount() + }) + + $(this.$body).find('.form-control').click(function(){ + me.idx = $(this).attr("idx"); + me.set_outstanding_amount(); + me.update_paid_amount(true); + }) + + $(this.$body).find('.write_off_amount').change(function(){ + me.write_off_amount(flt($(this).val()), precision("write_off_amount")); + }) + + $(this.$body).find('.change_amount').change(function(){ + me.change_amount(flt($(this).val()), precision("change_amount")); + }) + }, + + highlight_selected_row: function(){ + var me = this; + var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx})); + $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode') + selected_row.addClass('selected-payment-mode') + $(this.$body).find('.amount').attr('disabled', true); + this.selected_mode.attr('disabled', false); + }, + + bind_numeric_keys_event: function(){ + var me = this; + $(this.$body).find('.pos-keyboard-key').click(function(){ + me.payment_val += $(this).text(); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) + me.idx = me.selected_mode.attr("idx") + me.update_paid_amount() + }) + + $(this.$body).find('.delete-btn').click(function(){ + me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); + me.idx = me.selected_mode.attr("idx") + me.update_paid_amount(); + }) + + }, + + bind_amount_change_event: function(){ + var me = this; + this.selected_mode.change(function(){ + me.payment_val = flt($(this).val()) || 0.0; + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) + me.idx = me.selected_mode.attr("idx") + me.update_payment_amount() + }) + }, + + clear_amount: function() { + var me = this; + $(this.$body).find('.clr').click(function(e){ + e.stopPropagation(); + me.idx = $(this).attr("idx"); + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); + me.payment_val = 0.0; + me.selected_mode.val(0.0); + me.highlight_selected_row(); + me.update_payment_amount(); + }) + }, + + write_off_amount: function(write_off_amount) { + var me = this; + + this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount")); + this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, + precision("base_write_off_amount")); + this.calculate_outstanding_amount(false) + this.show_amounts() + }, + + change_amount: function(change_amount) { + var me = this; + + this.frm.doc.change_amount = flt(change_amount, precision("change_amount")); + this.calculate_write_off_amount() + this.show_amounts() + }, + + update_paid_amount: function(update_write_off) { + var me = this; + if(in_list(['change_amount', 'write_off_amount'], this.idx)){ + var value = me.selected_mode.val(); + if(me.idx == 'change_amount'){ + me.change_amount(value) + } else{ + if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { + value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx)); + } + me.write_off_amount(value) + } + }else{ + this.update_payment_amount() + } + }, + + update_payment_amount: function(){ + var me = this; + + $.each(this.frm.doc.payments, function(index, data){ + if(cint(me.idx) == cint(data.idx)){ + data.amount = flt(me.selected_mode.val(), 2) + } + }) + + this.calculate_outstanding_amount(false); + this.show_amounts(); + }, + + show_amounts: function(){ + var me = this; + $(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); + $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); + $(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)) + $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency)) + this.update_invoice(); + } +}) \ No newline at end of file From ac164c66ed3494368fcb074a5ec472b661914065 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 24 Nov 2020 16:09:11 +0530 Subject: [PATCH 146/283] perf: get recent transaction only when component is visible --- erpnext/selling/page/point_of_sale/pos_past_order_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index 166d9cf0ce..ec392313f5 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -62,7 +62,7 @@ erpnext.PointOfSale.PastOrderList = class { options: `Draft\nPaid\nConsolidated\nReturn`, placeholder: __('Filter by invoice status'), onchange: function() { - me.refresh_list(me.search_field.get_value(), this.value); + if (me.$component.is(':visible')) me.refresh_list(); } }, parent: this.$component.find('.status-field'), From 5a33f2c394aceb03be6b00e142e6dea25695d1d3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 24 Nov 2020 16:59:36 +0530 Subject: [PATCH 147/283] fix: bom stock report color showing always red --- .../manufacturing/report/bom_stock_report/bom_stock_report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 84f5c346ca..8cd016461c 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -26,7 +26,7 @@ frappe.query_reports["BOM Stock Report"] = { "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.id == "item") { - if (data["Enough Parts to Build"] > 0) { + if (data["enough_parts_to_build"] > 0) { value = `${data['item']}`; } else { value = `${data['item']}`; From e4755828c4690404939ad7a5cc53a0d6ca988e18 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 24 Nov 2020 23:31:06 +0530 Subject: [PATCH 148/283] fix: template errors in pricing rule (#23999) * fix: solve microtemplating errors --- .../doctype/pricing_rule/pricing_rule.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js index c92b58b580..d79ad5f528 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js @@ -42,56 +42,56 @@ frappe.ui.form.on('Pricing Rule', {

- ${__('Notes')} + {{__('Notes')}}

  • - ${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")} + {{__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}}
  • - ${__("If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")} + {{__("If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")}}
  • - ${__('Discount Percentage can be applied either against a Price List or for all Price List.')} + {{__('Discount Percentage can be applied either against a Price List or for all Price List.')}}
  • - ${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')} + {{__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}}

- ${__('How Pricing Rule is applied?')} + {{__('How Pricing Rule is applied?')}}

  1. - ${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")} + {{__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}}
  2. - ${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")} + {{__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")}}
  3. - ${__('Pricing Rules are further filtered based on quantity.')} + {{__('Pricing Rules are further filtered based on quantity.')}}
  4. - ${__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')} + {{__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')}}
  5. - ${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')} + {{__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}}
    • - ${__('Item Code > Item Group > Brand')} + {{__('Item Code > Item Group > Brand')}}
    • - ${__('Customer > Customer Group > Territory')} + {{__('Customer > Customer Group > Territory')}}
    • - ${__('Supplier > Supplier Type')} + {{__('Supplier > Supplier Type')}}
  6. - ${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')} + {{__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}}
"; + msg += ""; + for (let key in leave_allocations) { + msg += ""; + } + msg += "
"+__('Leave Type')+""+__("Leave Allocation")+""+__("Leaves Granted")+"
"+key+""+leave_allocations[key]["name"]+""+leave_allocations[key]["leaves"]+"
"; + return msg; + }, + + assignment_based_on: function(frm) { + if (frm.doc.assignment_based_on) { + frm.events.set_effective_date(frm); + } else { + frm.set_value("effective_from", ''); + frm.set_value("effective_to", ''); + } + }, + + leave_period: function(frm) { + if (frm.doc.leave_period) { + frm.events.set_effective_date(frm); + } + }, + + set_effective_date: function(frm) { + if (frm.doc.assignment_based_on == "Leave Period" && frm.doc.leave_period) { + frappe.model.with_doc("Leave Period", frm.doc.leave_period, function () { + let from_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "from_date"); + let to_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "to_date"); + frm.set_value("effective_from", from_date); + frm.set_value("effective_to", to_date); + + }); + } else if (frm.doc.assignment_based_on == "Joining Date" && frm.doc.employee) { + frappe.model.with_doc("Employee", frm.doc.employee, function () { + let from_date = frappe.model.get_value("Employee", frm.doc.employee, "date_of_joining"); + frm.set_value("effective_from", from_date); + frm.set_value("effective_to", frappe.datetime.add_months(frm.doc.effective_from, 12)); + }); + } + frm.refresh(); + } + +}); diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json new file mode 100644 index 0000000000..ecebb3b7d6 --- /dev/null +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json @@ -0,0 +1,160 @@ +{ + "actions": [], + "autoname": "HR-LPOL-ASSGN-.#####", + "creation": "2020-08-19 13:02:43.343666", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "company", + "leave_policy", + "carry_forward", + "column_break_5", + "assignment_based_on", + "leave_period", + "effective_from", + "effective_to", + "leaves_allocated", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee name", + "read_only": 1 + }, + { + "fieldname": "leave_policy", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Policy", + "options": "Leave Policy", + "reqd": 1 + }, + { + "fieldname": "assignment_based_on", + "fieldtype": "Select", + "label": "Assignment based on", + "options": "\nLeave Period\nJoining Date" + }, + { + "depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "fieldname": "leave_period", + "fieldtype": "Link", + "label": "Leave Period", + "mandatory_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "options": "Leave Period" + }, + { + "fieldname": "effective_from", + "fieldtype": "Date", + "label": "Effective From", + "read_only_depends_on": "eval:doc.assignment_based_on", + "reqd": 1 + }, + { + "fieldname": "effective_to", + "fieldtype": "Date", + "label": "Effective To", + "read_only_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Policy Assignment", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "carry_forward", + "fieldtype": "Check", + "label": "Add unused leaves from previous allocations" + }, + { + "default": "0", + "fieldname": "leaves_allocated", + "fieldtype": "Check", + "hidden": 1, + "label": "Leaves Allocated" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-10-15 15:18:15.227848", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Policy Assignment", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py new file mode 100644 index 0000000000..a5068bc26d --- /dev/null +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -0,0 +1,163 @@ +# -*- 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 +from frappe import _, bold +from frappe.utils import getdate, date_diff, comma_and, formatdate +from math import ceil +import json +from six import string_types + +class LeavePolicyAssignment(Document): + + def validate(self): + self.validate_policy_assignment_overlap() + self.set_dates() + + def set_dates(self): + if self.assignment_based_on == "Leave Period": + self.effective_from, self.effective_to = frappe.db.get_value("Leave Period", self.leave_period, ["from_date", "to_date"]) + elif self.assignment_based_on == "Joining Date": + self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining") + + def validate_policy_assignment_overlap(self): + leave_policy_assignments = frappe.get_all("Leave Policy Assignment", filters = { + "employee": self.employee, + "name": ("!=", self.name), + "docstatus": 1, + "effective_to": (">=", self.effective_from), + "effective_from": ("<=", self.effective_to) + }) + + if len(leave_policy_assignments): + frappe.throw(_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}") + .format(bold(self.leave_policy), bold(self.employee), bold(formatdate(self.effective_from)), bold(formatdate(self.effective_to)))) + + def grant_leave_alloc_for_employee(self): + if self.leaves_allocated: + frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment")) + else: + leave_allocations = {} + leave_type_details = get_leave_type_details() + + leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) + date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") + + for leave_policy_detail in leave_policy.leave_policy_details: + if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp: + leave_allocation, new_leaves_allocated = self.create_leave_allocation( + leave_policy_detail.leave_type, leave_policy_detail.annual_allocation, + leave_type_details, date_of_joining + ) + + leave_allocations[leave_policy_detail.leave_type] = {"name": leave_allocation, "leaves": new_leaves_allocated} + + self.db_set("leaves_allocated", 1) + return leave_allocations + + def create_leave_allocation(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): + # Creates leave allocation for the given employee in the provided leave period + carry_forward = self.carry_forward + if self.carry_forward and not leave_type_details.get(leave_type).is_carry_forward: + carry_forward = 0 + + new_leaves_allocated = self.get_new_leaves(leave_type, new_leaves_allocated, + leave_type_details, date_of_joining) + + allocation = frappe.get_doc(dict( + doctype="Leave Allocation", + employee=self.employee, + leave_type=leave_type, + from_date=self.effective_from, + to_date=self.effective_to, + new_leaves_allocated=new_leaves_allocated, + leave_period=self.leave_period or None, + leave_policy_assignment = self.name, + leave_policy = self.leave_policy, + carry_forward=carry_forward + )) + allocation.save(ignore_permissions = True) + allocation.submit() + return allocation.name, new_leaves_allocated + + def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): + # Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period + if getdate(date_of_joining) > getdate(self.effective_from): + remaining_period = ((date_diff(self.effective_to, date_of_joining) + 1) / (date_diff(self.effective_to, self.effective_from) + 1)) + new_leaves_allocated = ceil(new_leaves_allocated * remaining_period) + + # Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0 + if leave_type_details.get(leave_type).is_earned_leave == 1 or leave_type_details.get(leave_type).is_compensatory == 1: + new_leaves_allocated = 0 + + return new_leaves_allocated + +@frappe.whitelist() +def grant_leave_for_multiple_employees(leave_policy_assignments): + leave_policy_assignments = json.loads(leave_policy_assignments) + not_granted = [] + for assignment in leave_policy_assignments: + try: + frappe.get_doc("Leave Policy Assignment", assignment).grant_leave_alloc_for_employee() + except Exception: + not_granted.append(assignment) + + if len(not_granted): + msg = _("Leave not Granted for Assignments:")+ bold(comma_and(not_granted)) + _(". Please Check documents") + else: + msg = _("Leave granted Successfully") + frappe.msgprint(msg) + +@frappe.whitelist() +def create_assignment_for_multiple_employees(employees, data): + + if isinstance(employees, string_types): + employees= json.loads(employees) + + if isinstance(data, string_types): + data = frappe._dict(json.loads(data)) + + docs_name = [] + for employee in employees: + assignment = frappe.new_doc("Leave Policy Assignment") + assignment.employee = employee + assignment.assignment_based_on = data.assignment_based_on or None + assignment.leave_policy = data.leave_policy + assignment.effective_from = getdate(data.effective_from) or None + assignment.effective_to = getdate(data.effective_to) or None + assignment.leave_period = data.leave_period or None + assignment.carry_forward = data.carry_forward + + assignment.save() + assignment.submit() + docs_name.append(assignment.name) + return docs_name + + +def automatically_allocate_leaves_based_on_leave_policy(): + today = getdate() + automatically_allocate_leaves_based_on_leave_policy = frappe.db.get_single_value( + 'HR Settings', 'automatically_allocate_leaves_based_on_leave_policy' + ) + + pending_assignments = frappe.get_list( + "Leave Policy Assignment", + filters = {"docstatus": 1, "leaves_allocated": 0, "effective_from": today} + ) + + if len(pending_assignments) and automatically_allocate_leaves_based_on_leave_policy: + for assignment in pending_assignments: + frappe.get_doc("Leave Policy Assignment", assignment.name).grant_leave_alloc_for_employee() + + +def get_leave_type_details(): + leave_type_details = frappe._dict() + leave_types = frappe.get_all("Leave Type", + fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carry_forwarded_leaves_after_days"]) + for d in leave_types: + leave_type_details.setdefault(d.name, d) + return leave_type_details + diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js new file mode 100644 index 0000000000..468f243885 --- /dev/null +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js @@ -0,0 +1,138 @@ +frappe.listview_settings['Leave Policy Assignment'] = { + onload: function (list_view) { + let me = this; + list_view.page.add_inner_button(__("Bulk Leave Policy Assignment"), function () { + me.dialog = new frappe.ui.form.MultiSelectDialog({ + doctype: "Employee", + target: cur_list, + setters: { + company: '', + department: '', + }, + data_fields: [{ + fieldname: 'leave_policy', + fieldtype: 'Link', + options: 'Leave Policy', + label: __('Leave Policy'), + reqd: 1 + }, + { + fieldname: 'assignment_based_on', + fieldtype: 'Select', + options: ["", "Leave Period"], + label: __('Assignment Based On'), + onchange: () => { + if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period") { + cur_dialog.set_df_property("effective_from", "read_only", 1); + cur_dialog.set_df_property("leave_period", "reqd", 1); + cur_dialog.set_df_property("effective_to", "read_only", 1); + } else { + cur_dialog.set_df_property("effective_from", "read_only", 0); + cur_dialog.set_df_property("leave_period", "reqd", 0); + cur_dialog.set_df_property("effective_to", "read_only", 0); + cur_dialog.set_value("effective_from", ""); + cur_dialog.set_value("effective_to", ""); + } + } + }, + { + fieldname: "leave_period", + fieldtype: 'Link', + options: "Leave Period", + label: __('Leave Period'), + depends_on: doc => { + return doc.assignment_based_on == 'Leave Period'; + }, + onchange: () => { + if (cur_dialog.fields_dict.leave_period.value) { + me.set_effective_date(); + } + } + }, + { + fieldtype: "Column Break" + }, + { + fieldname: 'effective_from', + fieldtype: 'Date', + label: __('Effective From'), + reqd: 1 + }, + { + fieldname: 'effective_to', + fieldtype: 'Date', + label: __('Effective To'), + reqd: 1 + }, + { + fieldname: 'carry_forward', + fieldtype: 'Check', + label: __('Add unused leaves from previous allocations') + } + ], + get_query() { + return { + filters: { + status: ['=', 'Active'] + } + }; + }, + add_filters_group: 1, + primary_action_label: "Assign", + action(employees, data) { + frappe.call({ + method: 'erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.create_assignment_for_multiple_employees', + async: false, + args: { + employees: employees, + data: data + } + }); + cur_dialog.hide(); + } + }); + }); + + list_view.page.add_inner_button(__("Grant Leaves"), function () { + me.dialog = new frappe.ui.form.MultiSelectDialog({ + doctype: "Leave Policy Assignment", + target: cur_list, + setters: { + company: '', + employee: '', + }, + get_query() { + return { + filters: { + docstatus: ['=', 1], + leaves_allocated: ['=', 0] + } + }; + }, + add_filters_group: 1, + primary_action_label: "Grant Leaves", + action(leave_policy_assignments) { + frappe.call({ + method: 'erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.grant_leave_for_multiple_employees', + async: false, + args: { + leave_policy_assignments: leave_policy_assignments + } + }); + me.dialog.hide(); + } + }); + }); + }, + + set_effective_date: function () { + if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period" && cur_dialog.fields_dict.leave_period.value) { + frappe.model.with_doc("Leave Period", cur_dialog.fields_dict.leave_period.value, function () { + let from_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "from_date"); + let to_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "to_date"); + cur_dialog.set_value("effective_from", from_date); + cur_dialog.set_value("effective_to", to_date); + }); + } + } +}; \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py new file mode 100644 index 0000000000..c7bc6fb775 --- /dev/null +++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from erpnext.hr.doctype.leave_application.test_leave_application import get_leave_period, get_employee +from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees +from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy + +class TestLeavePolicyAssignment(unittest.TestCase): + + def setUp(self): + for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: + frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec + + def test_grant_leaves(self): + leave_period = get_leave_period() + employee = get_employee() + + # create the leave policy with leave type "_Test Leave Type", allocation = 10 + leave_policy = create_leave_policy() + leave_policy.submit() + + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name + } + + leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) + + leave_policy_assignment_doc = frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]) + leave_policy_assignment_doc.grant_leave_alloc_for_employee() + leave_policy_assignment_doc.reload() + + self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 1) + + leave_allocation = frappe.get_list("Leave Allocation", filters={ + "employee": employee.name, + "leave_policy":leave_policy.name, + "leave_policy_assignment": leave_policy_assignments[0], + "docstatus": 1})[0] + + leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) + + self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10) + self.assertEqual(leave_alloc_doc.leave_type, "_Test Leave Type") + self.assertEqual(leave_alloc_doc.from_date, leave_period.from_date) + self.assertEqual(leave_alloc_doc.to_date, leave_period.to_date) + self.assertEqual(leave_alloc_doc.leave_policy, leave_policy.name) + self.assertEqual(leave_alloc_doc.leave_policy_assignment, leave_policy_assignments[0]) + + def test_allow_to_grant_all_leave_after_cancellation_of_every_leave_allocation(self): + leave_period = get_leave_period() + employee = get_employee() + + # create the leave policy with leave type "_Test Leave Type", allocation = 10 + leave_policy = create_leave_policy() + leave_policy.submit() + + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name + } + + leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) + + leave_policy_assignment_doc = frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]) + leave_policy_assignment_doc.grant_leave_alloc_for_employee() + leave_policy_assignment_doc.reload() + + + # every leave is allocated no more leave can be granted now + self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 1) + + leave_allocation = frappe.get_list("Leave Allocation", filters={ + "employee": employee.name, + "leave_policy":leave_policy.name, + "leave_policy_assignment": leave_policy_assignments[0], + "docstatus": 1})[0] + + leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) + + # User all allowed to grant leave when there is no allocation against assignment + leave_alloc_doc.cancel() + leave_alloc_doc.delete() + + leave_policy_assignment_doc.reload() + + + # User are now allowed to grant leave + self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 0) + + def tearDown(self): + for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: + frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec + + diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 4a135e0ffe..a2092919f8 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -33,6 +33,7 @@ "is_earned_leave", "earned_leave_frequency", "column_break_22", + "based_on_date_of_joining", "rounding" ], "fields": [ @@ -189,6 +190,13 @@ }, { "default": "0", + "depends_on": "eval:doc.is_earned_leave", + "description": "If checked, leave will be granted on the day of joining every month.", + "fieldname": "based_on_date_of_joining", + "fieldtype": "Check", + "label": "Based On Date Of Joining" + }, + { "depends_on": "eval:doc.is_lwp == 0", "fieldname": "is_ppl", "fieldtype": "Check", @@ -205,7 +213,7 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2020-08-26 14:04:54.318687", + "modified": "2020-10-15 15:49:47.555105", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 8d95924681..d700e7fccf 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -215,19 +215,6 @@ def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date): + _(") for {0}").format(exists_for) frappe.throw(msg) -def get_employee_leave_policy(employee): - leave_policy = frappe.db.get_value("Employee", employee, "leave_policy") - if not leave_policy: - employee_grade = frappe.db.get_value("Employee", employee, "grade") - if employee_grade: - leave_policy = frappe.db.get_value("Employee Grade", employee_grade, "default_leave_policy") - if not leave_policy: - frappe.throw(_("Employee {0} of grade {1} have no default leave policy").format(employee, employee_grade)) - if leave_policy: - return frappe.get_doc("Leave Policy", leave_policy) - else: - frappe.throw(_("Please set leave policy for employee {0} in Employee / Grade record").format(employee)) - def validate_duplicate_exemption_for_payroll_period(doctype, docname, payroll_period, employee): existing_record = frappe.db.exists(doctype, { "payroll_period": payroll_period, @@ -300,43 +287,68 @@ def generate_leave_encashment(): def allocate_earned_leaves(): '''Allocate earned leaves to Employees''' - e_leave_types = frappe.get_all("Leave Type", - fields=["name", "max_leaves_allowed", "earned_leave_frequency", "rounding"], - filters={'is_earned_leave' : 1}) + e_leave_types = get_earned_leaves() today = getdate() - divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} for e_leave_type in e_leave_types: - leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where %s - between from_date and to_date and docstatus=1 and leave_type=%s""", (today, e_leave_type.name), as_dict=1) + + leave_allocations = get_leave_allocations(today, e_leave_type.name) + for allocation in leave_allocations: - leave_policy = get_employee_leave_policy(allocation.employee) - if not leave_policy: + + if not allocation.leave_policy_assignment and not allocation.leave_policy: continue - if not e_leave_type.earned_leave_frequency == "Monthly": - if not check_frequency_hit(allocation.from_date, today, e_leave_type.earned_leave_frequency): - continue + + leave_policy = allocation.leave_policy if allocation.leave_policy else frappe.db.get_value( + "Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"]) + annual_allocation = frappe.db.get_value("Leave Policy Detail", filters={ - 'parent': leave_policy.name, + 'parent': leave_policy, 'leave_type': e_leave_type.name }, fieldname=['annual_allocation']) - if annual_allocation: - earned_leaves = flt(annual_allocation) / divide_by_frequency[e_leave_type.earned_leave_frequency] - if e_leave_type.rounding == "0.5": - earned_leaves = round(earned_leaves * 2) / 2 - else: - earned_leaves = round(earned_leaves) - allocation = frappe.get_doc('Leave Allocation', allocation.name) - new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) + from_date=allocation.from_date - if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: - new_allocation = e_leave_type.max_leaves_allowed + if e_leave_type.based_on_date_of_joining_date: + from_date = frappe.db.get_value("Employee", allocation.employee, "date_of_joining") - if new_allocation == allocation.total_leaves_allocated: - continue - allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) - create_additional_leave_ledger_entry(allocation, earned_leaves, today) + if check_effective_date(from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining_date): + update_previous_leave_allocation(allocation, annual_allocation, e_leave_type) + +def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type): + divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} + if annual_allocation: + earned_leaves = flt(annual_allocation) / divide_by_frequency[e_leave_type.earned_leave_frequency] + if e_leave_type.rounding == "0.5": + earned_leaves = round(earned_leaves * 2) / 2 + else: + earned_leaves = round(earned_leaves) + + allocation = frappe.get_doc('Leave Allocation', allocation.name) + new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) + + if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0: + new_allocation = e_leave_type.max_leaves_allowed + + if new_allocation != allocation.total_leaves_allocated: + allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) + today_date = today() + create_additional_leave_ledger_entry(allocation, earned_leaves, today_date) + + +def get_leave_allocations(date, leave_type): + return frappe.db.sql("""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy + from `tabLeave Allocation` + where + %s between from_date and to_date and docstatus=1 + and leave_type=%s""", + (date, leave_type), as_dict=1) + + +def get_earned_leaves(): + return frappe.get_all("Leave Type", + fields=["name", "max_leaves_allowed", "earned_leave_frequency", "rounding", "based_on_date_of_joining"], + filters={'is_earned_leave' : 1}) def create_additional_leave_ledger_entry(allocation, leaves, date): ''' Create leave ledger entry for leave types ''' @@ -345,24 +357,32 @@ def create_additional_leave_ledger_entry(allocation, leaves, date): allocation.unused_leaves = 0 allocation.create_leave_ledger_entry() -def check_frequency_hit(from_date, to_date, frequency): - '''Return True if current date matches frequency''' - from_dt = get_datetime(from_date) - to_dt = get_datetime(to_date) +def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining_date): + import calendar from dateutil import relativedelta - rd = relativedelta.relativedelta(to_dt, from_dt) - months = rd.months - if frequency == "Quarterly": - if not months % 3: + + from_date = get_datetime(from_date) + to_date = get_datetime(to_date) + rd = relativedelta.relativedelta(to_date, from_date) + #last day of month + last_day = calendar.monthrange(to_date.year, to_date.month)[1] + + if (from_date.day == to_date.day and based_on_date_of_joining_date) or (not based_on_date_of_joining_date and to_date.day == last_day): + if frequency == "Monthly": return True - elif frequency == "Half-Yearly": - if not months % 6: + elif frequency == "Quarterly" and rd.months % 3: return True - elif frequency == "Yearly": - if not months % 12: + elif frequency == "Half-Yearly" and rd.months % 6: return True + elif frequency == "Yearly" and rd.months % 12: + return True + + if frappe.flags.in_test: + return True + return False + def get_salary_assignment(employee, date): assignment = frappe.db.sql(""" select * from `tabSalary Structure Assignment` @@ -454,3 +474,10 @@ def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, co if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0: total_claimed_amount = sum_of_claimed_amount[0].total_amount return total_claimed_amount + +def grant_leaves_automatically(): + automatically_allocate_leaves_based_on_leave_policy = frappe.db.get_singles_value("HR Settings", "automatically_allocate_leaves_based_on_leave_policy") + if automatically_allocate_leaves_based_on_leave_policy: + lpa = frappe.db.get_all("Leave Policy Assignment", filters={"effective_from": getdate(), "docstatus": 1, "leaves_allocated":0}) + for assignment in lpa: + frappe.get_doc("Leave Policy Assignment", assignment.name).grant_leave_alloc_for_employee() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 25be884117..98b2fcdcab 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -735,3 +735,4 @@ erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail erpnext.patches.v13_0.update_reason_for_resignation_in_employee erpnext.patches.v13_0.update_custom_fields_for_shopify execute:frappe.delete_doc("Report", "Quoted Item Comparison") +erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy diff --git a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py new file mode 100644 index 0000000000..80c9137653 --- /dev/null +++ b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py @@ -0,0 +1,77 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + if "leave_policy" in frappe.db.get_table_columns("Employee"): + employees_with_leave_policy = frappe.db.sql("SELECT name, leave_policy FROM `tabEmployee` WHERE leave_policy IS NOT NULL and leave_policy != ''", as_dict = 1) + + employee_with_assignment = [] + leave_policy =[] + + #for employee + + for employee in employees_with_leave_policy: + alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": employee.leave_policy, "docstatus": 1}) + if not alloc: + create_assignment(employee.name, employee.leave_policy) + + employee_with_assignment.append(employee.name) + leave_policy.append(employee.leave_policy) + + + if "default_leave_policy" in frappe.db.get_table_columns("Employee"): + employee_grade_with_leave_policy = frappe.db.sql("SELECT name, default_leave_policy FROM `tabEmployee Grade` WHERE default_leave_policy IS NOT NULL and default_leave_policy!=''", as_dict = 1) + + #for whole employee Grade + + for grade in employee_grade_with_leave_policy: + employees = get_employee_with_grade(grade.name) + for employee in employees: + + if employee not in employee_with_assignment: #Will ensure no duplicate + alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": grade.default_leave_policy, "docstatus": 1}) + if not alloc: + create_assignment(employee.name, grade.default_leave_policy) + leave_policy.append(grade.default_leave_policy) + + #for old Leave allocation and leave policy from allocation, which may got updated in employee grade. + leave_allocations = frappe.db.sql("SELECT leave_policy, leave_period, employee FROM `tabLeave Allocation` WHERE leave_policy IS NOT NULL and leave_policy != '' and docstatus = 1 ", as_dict = 1) + + for allocation in leave_allocations: + if allocation.leave_policy not in leave_policy: + create_assignment(allocation.employee, allocation.leave_policy, leave_period=allocation.leave_period, + allocation_exists=True) + +def create_assignment(employee, leave_policy, leave_period=None, allocation_exists = False): + + filters = {"employee":employee, "leave_policy": leave_policy} + if leave_period: + filters["leave_period"] = leave_period + + if not frappe.db.exists("Leave Policy Assignment" , filters): + lpa = frappe.new_doc("Leave Policy Assignment") + lpa.employee = employee + lpa.leave_policy = leave_policy + + lpa.flags.ignore_mandatory = True + if allocation_exists: + lpa.assignment_based_on = 'Leave Period' + lpa.leave_period = leave_period + lpa.leaves_allocated = 1 + + lpa.save() + if allocation_exists: + lpa.submit() + #Updating old Leave Allocation + frappe.db.sql("Update `tabLeave Allocation` set leave_policy_assignment = %s", lpa.name) + + +def get_employee_with_grade(grade): + return frappe.get_list("Employee", filters = {"grade": grade}) + + + From 54228691e7e64ac181cb308520708e9f97a2694c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 26 Nov 2020 16:38:28 +0530 Subject: [PATCH 160/283] feat(IPME): Button to create Stock Entry for Drug Shortage --- .../inpatient_medication_entry.js | 23 +++++ .../inpatient_medication_entry.py | 92 +++++++++++++++---- 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js index f523cf21bd..3980370370 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js @@ -29,6 +29,29 @@ frappe.ui.form.on('Inpatient Medication Entry', { } }; }); + + if (frm.doc.__islocal || frm.doc.docstatus !== 0) + return; + + frm.add_custom_button(__('Make Stock Entry'), function() { + frappe.call({ + method: 'erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry.make_difference_stock_entry', + args: { docname: frm.doc.name }, + freeze: true, + callback: function(r) { + if (r.message) { + var doclist = frappe.model.sync(r.message); + frappe.set_route('Form', doclist[0].doctype, doclist[0].name); + } else { + frappe.msgprint({ + title: __('No Drug Shortage'), + message: __('All the drugs are available with sufficient qty to process this Inpatient Medication Entry.'), + indicator: 'green' + }); + } + } + }) + }); }, patient: function(frm) { diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py index 5dac23abd9..5a2a0e54aa 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py @@ -142,25 +142,32 @@ class InpatientMedicationEntry(Document): return orders, order_entry_map def check_stock_qty(self): - from erpnext.stock.stock_ledger import NegativeStockError + drug_shortage = get_drug_shortage_map(self.medication_orders, self.warehouse) - drug_availability = dict() - for d in self.medication_orders: - if not drug_availability.get(d.drug_code): - drug_availability[d.drug_code] = 0 - drug_availability[d.drug_code] += flt(d.dosage) + if drug_shortage: + message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse)) + message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.') - for drug, dosage in drug_availability.items(): - available_qty = get_latest_stock_qty(drug, self.warehouse) + formatted_item_rows = '' - # validate qty - if flt(available_qty) < flt(dosage): - frappe.throw(_('Quantity not available for {0} in warehouse {1}').format( - frappe.bold(drug), frappe.bold(self.warehouse)) - + '

' + _('Available quantity is {0}, you need {1}').format( - frappe.bold(available_qty), frappe.bold(dosage)) - + '

' + _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.'), - NegativeStockError, title=_('Insufficient Stock')) + for drug, shortage_qty in drug_shortage.items(): + item_link = get_link_to_form('Item', drug) + formatted_item_rows += """ + {0} + {1} + """.format(item_link, frappe.bold(shortage_qty)) + + message += """ + + + + + + {2} +
{0}{1}
+ """.format(_('Drug Code'), _('Shortage Qty'), formatted_item_rows) + + frappe.throw(message, title=_('Insufficient Stock'), is_minimizable=True, wide=True) def make_stock_entry(self): stock_entry = frappe.new_doc('Stock Entry') @@ -276,4 +283,55 @@ def get_current_healthcare_service_unit(inpatient_record): ip_record = frappe.get_doc('Inpatient Record', inpatient_record) if ip_record.inpatient_occupancies: return ip_record.inpatient_occupancies[-1].service_unit - return \ No newline at end of file + return + + +def get_drug_shortage_map(medication_orders, warehouse): + """ + Returns a dict like { drug_code: shortage_qty } + """ + drug_requirement = dict() + for d in medication_orders: + if not drug_requirement.get(d.drug_code): + drug_requirement[d.drug_code] = 0 + drug_requirement[d.drug_code] += flt(d.dosage) + + drug_shortage = dict() + for drug, required_qty in drug_requirement.items(): + available_qty = get_latest_stock_qty(drug, warehouse) + if flt(required_qty) > flt(available_qty): + drug_shortage[drug] = flt(flt(required_qty) - flt(available_qty)) + + return drug_shortage + + +@frappe.whitelist() +def make_difference_stock_entry(docname): + doc = frappe.get_doc('Inpatient Medication Entry', docname) + drug_shortage = get_drug_shortage_map(doc.medication_orders, doc.warehouse) + + if not drug_shortage: + return None + + stock_entry = frappe.new_doc('Stock Entry') + stock_entry.purpose = 'Material Transfer' + stock_entry.set_stock_entry_type() + stock_entry.to_warehouse = doc.warehouse + stock_entry.company = doc.company + cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center') + expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company) + + for drug, shortage_qty in drug_shortage.items(): + se_child = stock_entry.append('items') + se_child.item_code = drug + se_child.item_name = frappe.db.get_value('Item', drug, 'stock_uom') + se_child.uom = frappe.db.get_value('Item', drug, 'stock_uom') + se_child.stock_uom = se_child.uom + se_child.qty = flt(shortage_qty) + se_child.t_warehouse = doc.warehouse + # in stock uom + se_child.conversion_factor = 1 + se_child.cost_center = cost_center + se_child.expense_account = expense_account + + return stock_entry From ac8ee249d544952b204e62fa40351652404ed9fd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 26 Nov 2020 22:11:20 +0530 Subject: [PATCH 161/283] feat: Medication doctypes added to Desk page and Patient dashboard --- erpnext/healthcare/desk_page/healthcare/healthcare.json | 9 +++++++-- .../inpatient_medication_entry.py | 3 ++- erpnext/healthcare/doctype/patient/patient_dashboard.py | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 81d60481ce..af601f3eb2 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -30,6 +30,11 @@ "label": "Laboratory", "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]" }, + { + "hidden": 0, + "label": "Inpatient", + "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]" + }, { "hidden": 0, "label": "Rehabilitation and Physiotherapy", @@ -38,7 +43,7 @@ { "hidden": 0, "label": "Records and History", - "links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]" + "links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t}\n]" }, { "hidden": 0, @@ -64,7 +69,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-11-23 23:00:48.764377", + "modified": "2020-11-26 22:09:09.164584", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py index 5a2a0e54aa..70ae713866 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py @@ -230,7 +230,8 @@ def get_pending_medication_orders(entry): for doc in data: inpatient_record = doc.inpatient_record - doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record) + if inpatient_record: + doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record) if entry.service_unit and doc.service_unit != entry.service_unit: to_remove.append(doc) diff --git a/erpnext/healthcare/doctype/patient/patient_dashboard.py b/erpnext/healthcare/doctype/patient/patient_dashboard.py index e3def72334..39603f77a0 100644 --- a/erpnext/healthcare/doctype/patient/patient_dashboard.py +++ b/erpnext/healthcare/doctype/patient/patient_dashboard.py @@ -18,6 +18,10 @@ def get_data(): { 'label': _('Billing'), 'items': ['Sales Invoice'] + }, + { + 'label': _('Orders'), + 'items': ['Inpatient Medication Order'] } ] } From f5eddce407e46979aa279d709bae05d23417ade7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 27 Nov 2020 12:27:17 +0530 Subject: [PATCH 162/283] test: Inpatient Medication Entry Drug Shortage --- .../inpatient_medication_entry.js | 2 +- .../test_inpatient_medication_entry.py | 41 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js index 3980370370..57af9eb848 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js @@ -50,7 +50,7 @@ frappe.ui.form.on('Inpatient Medication Entry', { }); } } - }) + }); }); }, diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py index 2f1bb6b56f..7cb5a4814e 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py @@ -9,6 +9,7 @@ from frappe.utils import add_days, getdate, now_datetime from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme +from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_drug_shortage_map, make_difference_stock_entry from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account class TestInpatientMedicationEntry(unittest.TestCase): @@ -82,6 +83,39 @@ class TestInpatientMedicationEntry(unittest.TestCase): self.assertEqual(stock_entry.items[0].patient, self.patient) self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name) + def test_drug_shortage_stock_entry(self): + ipmo = create_ipmo(self.patient) + ipmo.submit() + ipmo.reload() + + date = add_days(getdate(), -1) + filters = frappe._dict( + from_date=date, + to_date=date, + from_time='', + to_time='', + item_code='Dextromethorphan', + patient=self.patient + ) + + # check drug shortage + ipme = create_ipme(filters, update_stock=1) + ipme.warehouse = 'Finished Goods - _TC' + ipme.save() + drug_shortage = get_drug_shortage_map(ipme.medication_orders, ipme.warehouse) + self.assertEqual(drug_shortage.get('Dextromethorphan'), 3) + + # check material transfer for drug shortage + make_stock_entry() + stock_entry = make_difference_stock_entry(ipme.name) + self.assertEqual(stock_entry.items[0].item_code, 'Dextromethorphan') + self.assertEqual(stock_entry.items[0].qty, 3) + stock_entry.from_warehouse = 'Stores - _TC' + stock_entry.submit() + + ipme.reload() + ipme.submit() + def tearDown(self): # cleanup - Discharge schedule_discharge(frappe.as_json({'patient': self.patient})) @@ -94,15 +128,12 @@ class TestInpatientMedicationEntry(unittest.TestCase): for entry in frappe.get_all('Inpatient Medication Entry'): doc = frappe.get_doc('Inpatient Medication Entry', entry.name) doc.cancel() - frappe.db.delete('Stock Entry', {'inpatient_medication_entry': doc.name}) - doc.delete() for entry in frappe.get_all('Inpatient Medication Order'): doc = frappe.get_doc('Inpatient Medication Order', entry.name) doc.cancel() - doc.delete() -def make_stock_entry(): +def make_stock_entry(warehouse=None): frappe.db.set_value('Company', '_Test Company', { 'stock_adjustment_account': 'Stock Adjustment - _TC', 'default_inventory_account': 'Stock In Hand - _TC' @@ -110,7 +141,7 @@ def make_stock_entry(): stock_entry = frappe.new_doc('Stock Entry') stock_entry.stock_entry_type = 'Material Receipt' stock_entry.company = '_Test Company' - stock_entry.to_warehouse = 'Stores - _TC' + stock_entry.to_warehouse = warehouse or 'Stores - _TC' expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company') se_child = stock_entry.append('items') se_child.item_code = 'Dextromethorphan' From cf012ca9c3734acfe53d952bf55e61cf7223e413 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 27 Nov 2020 12:39:33 +0530 Subject: [PATCH 163/283] fix: show stock entry button only if update stock is enabled --- .../inpatient_medication_entry/inpatient_medication_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js index 57af9eb848..ca97489b8d 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js @@ -30,7 +30,7 @@ frappe.ui.form.on('Inpatient Medication Entry', { }; }); - if (frm.doc.__islocal || frm.doc.docstatus !== 0) + if (frm.doc.__islocal || frm.doc.docstatus !== 0 || !frm.doc.update_stock) return; frm.add_custom_button(__('Make Stock Entry'), function() { From 31ac7d982a0a90ade9b82c0c8673e652e70224d0 Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 27 Nov 2020 15:58:07 +0530 Subject: [PATCH 164/283] chore(GitHub): Add issue template config --- .github/ISSUE_TEMPLATE/config.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..26bb7ab280 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Community Forum + url: https://discuss.erpnext.com/ + about: For general QnA, discussions and community help. From 38e4635a104e401f2a50c52a3c669c558abb0cc4 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 29 Nov 2020 20:26:26 +0530 Subject: [PATCH 165/283] fix: import taxjar globally in the taxjar_integration module --- erpnext/erpnext_integrations/taxjar_integration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index 24fc3d44b9..f960998c3c 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -1,5 +1,7 @@ import traceback +import taxjar + import frappe from erpnext import get_default_company from frappe import _ @@ -29,7 +31,6 @@ def get_client(): def create_transaction(doc, method): - import taxjar """Create an order transaction in TaxJar""" if not TAXJAR_CREATE_TRANSACTIONS: From 2d5530da96237a2d746496efc7911b810f7e4047 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 29 Nov 2020 22:17:47 +0530 Subject: [PATCH 166/283] fix: Invoice discounting test --- .../invoice_discounting/invoice_discounting.py | 11 ++++++----- .../invoice_discounting/test_invoice_discounting.py | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 8083b21f75..af8940cde5 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -137,11 +137,12 @@ class InvoiceDiscounting(AccountsController): "cost_center": erpnext.get_default_cost_center(self.company) }) - je.append("accounts", { - "account": self.bank_charges_account, - "debit_in_account_currency": flt(self.bank_charges), - "cost_center": erpnext.get_default_cost_center(self.company) - }) + if self.bank_charges: + je.append("accounts", { + "account": self.bank_charges_account, + "debit_in_account_currency": flt(self.bank_charges), + "cost_center": erpnext.get_default_cost_center(self.company) + }) je.append("accounts", { "account": self.short_term_loan, diff --git a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py index 3d74d9a3b2..919dd0cba7 100644 --- a/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/test_invoice_discounting.py @@ -80,6 +80,7 @@ class TestInvoiceDiscounting(unittest.TestCase): short_term_loan=self.short_term_loan, bank_charges_account=self.bank_charges_account, bank_account=self.bank_account, + bank_charges=100 ) je = inv_disc.create_disbursement_entry() @@ -289,6 +290,7 @@ def create_invoice_discounting(invoices, **args): inv_disc.bank_account=args.bank_account inv_disc.loan_start_date = args.start or nowdate() inv_disc.loan_period = args.period or 30 + inv_disc.bank_charges = flt(args.bank_charges) for d in invoices: inv_disc.append("invoices", { From 452cbcd6eaa1d63538261ab91296b3cf2116ff79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 30 Nov 2020 10:55:12 +0530 Subject: [PATCH 167/283] fix: Update payments directly in Loan Interest Accrual --- erpnext/patches/v13_0/update_old_loans.py | 40 ++++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c4d9bdb7af..de29d329d1 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -5,6 +5,7 @@ from frappe.utils import nowdate from erpnext.accounts.doctype.account.test_account import create_account from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans from erpnext.loan_management.doctype.loan.loan import make_repayment_entry +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import get_accrued_interest_entries from frappe.model.naming import make_autoname def execute(): @@ -100,25 +101,32 @@ def execute(): process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) - payments = frappe.db.sql(''' SELECT j.name, a.credit, a.credit_in_account_currency, j.posting_date - FROM `tabJournal Entry` j, `tabJournal Entry Account` a - WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s - and a.account = %s and j.docstatus = 1 - ''', (loan.name, loan.loan_account), as_dict=1) + accrued_entries = get_accrued_interest_entries(loan.name) + total_principal, total_interest = frappe.db.get_value('Repayment Schedule', fields=['sum(principal_amount) as total_principal', + 'sum(interest_amount) as total_interest'], filters={'is_paid': 1, 'parent': loan.name}) - for payment in payments: - if payment.credit_in_account_currency: - repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan_type, loan.company) + for entry in accrued_entries: + interest_paid = 0 + principal_paid = 0 - repayment_entry.amount_paid = payment.credit_in_account_currency - repayment_entry.posting_date = payment.posting_date - repayment_entry.save() - repayment_entry.submit() + if total_interest > entry.interest_amount: + interest_paid = entry.interest_amount + else: + interest_paid = total_interest - jv = frappe.get_doc('Journal Entry', payment.name) - jv.flags.ignore_links = True - jv.cancel() + if total_principal > entry.payable_principal_amount: + principal_paid = entry.payable_principal_amount + else: + principal_paid = total_principal + + frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` + SET paid_principal_amount = `paid_principal_amount` + %s, + paid_interest_amount = `paid_interest_amount` + %s + WHERE name = %s""", + (principal_paid, interest_paid, entry.name)) + + total_principal -= principal_paid + total_interest -= interest_paid def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc('Loan Type') From 724e16bca120954049ba4a9532ef25b6446ddf19 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 30 Nov 2020 12:42:25 +0530 Subject: [PATCH 168/283] chore: Use JSON style response and use ORM - Use JSON style response for report columns - Use ORM instead of frappe.db.sql - Remove returned % from list view --- .../delivered_items_to_be_billed.py | 100 +++++++++++++++--- .../received_items_to_be_billed.py | 100 +++++++++++++++--- .../controllers/sales_and_purchase_return.py | 43 +++----- .../doctype/delivery_note/delivery_note.json | 3 +- .../purchase_receipt/purchase_receipt.json | 3 +- .../purchase_receipt/purchase_receipt.py | 22 ++-- 6 files changed, 203 insertions(+), 68 deletions(-) diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py index 2aea3f6423..515fd995e6 100644 --- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py +++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py @@ -14,19 +14,93 @@ def execute(filters=None): def get_column(): return [ - _("Delivery Note") + ":Link/Delivery Note:160", - _("Date") + ":Date:100", - _("Customer") + ":Link/Customer:120", - _("Customer Name") + "::120", - _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", - _("Billed Amount") + ":Currency:100", - _("Returned Amount") + ":Currency:120", - _("Pending Amount") + ":Currency:100", - _("Item Name") + "::120", - _("Description") + "::120", - _("Project") + ":Link/Project:120", - _("Company") + ":Link/Company:120", + { + "label": _("Delivery Note"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Delivery Note", + "width": 160 + }, + { + "label": _("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Customer"), + "fieldname": "customer", + "fieldtype": "Link", + "options": "Customer", + "width": 120 + }, + { + "label": _("Customer Name"), + "fieldname": "customer_name", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency" + }, + { + "label": _("Returned Amount"), + "fieldname": "returned_amount", + "fieldtype": "Currency", + "width": 120, + "options": "Company:company:default_currency" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 120, + "options": "Company:company:default_currency" + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Description"), + "fieldname": "description", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "width": 120 + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120 + } ] def get_args(): diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py index c7d4384a73..e9e9c9c4e6 100644 --- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py +++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py @@ -14,19 +14,93 @@ def execute(filters=None): def get_column(): return [ - _("Purchase Receipt") + ":Link/Purchase Receipt:160", - _("Date") + ":Date:100", - _("Supplier") + ":Link/Supplier:120", - _("Supplier Name") + "::120", - _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", - _("Billed Amount") + ":Currency:100", - _("Returned Amount") + ":Currency:120", - _("Pending Amount") + ":Currency:120", - _("Item Name") + "::120", - _("Description") + "::120", - _("Project") + ":Link/Project:120", - _("Company") + ":Link/Company:120", + { + "label": _("Purchase Receipt"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Purchase Receipt", + "width": 160 + }, + { + "label": _("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": 120 + }, + { + "label": _("Supplier Name"), + "fieldname": "supplier_name", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency" + }, + { + "label": _("Returned Amount"), + "fieldname": "returned_amount", + "fieldtype": "Currency", + "width": 120, + "options": "Company:company:default_currency" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 120, + "options": "Company:company:default_currency" + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Description"), + "fieldname": "description", + "fieldtype": "Data", + "width": 120 + }, + { + "label": _("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "width": 120 + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120 + } ] def get_args(): diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index e11289d79e..5299b25601 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -206,35 +206,26 @@ def get_already_returned_items(doc): def get_returned_qty_map_for_row(row_name, doctype): child_doctype = doctype + " Item" reference_field = frappe.scrub(child_doctype) if doctype == "Purchase Receipt" else "dn_detail" - reference_field = "child." + reference_field - columns = "" + + fields = [ + "sum(abs(`tab{0}`.qty)) as qty".format(child_doctype), + "sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype) + ] if doctype == "Purchase Receipt": - columns += ", sum(abs(child.rejected_qty)) as rejected_qty, \ - sum(abs(child.received_qty)) as received_qty, \ - sum(abs(child.received_stock_qty)) as received_stock_qty" + fields += [ + "sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype), + "sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype), + "sum(abs(`tab{0}`.received_stock_qty)) as received_stock_qty".format(child_doctype) + ] - data = frappe.db.sql(""" - select - sum(abs(child.qty)) as qty, - sum(abs(child.stock_qty)) as stock_qty, - %(columns)s - from - `tab{0}` child, `tab{1}` parent - where - child.parent = parent.name - and parent.docstatus = 1 - and parent.is_return = 1 - and {2} = %(row_name)s - """.format(child_doctype, doctype, reference_field), - { - "row_name": row_name, - "columns": columns, - "child_doctype": child_doctype, - "doctype": doctype, - "reference_field": reference_field - }, - as_dict=1) + data = frappe.db.get_list(doctype, + fields = fields, + filters = [ + [doctype, "docstatus", "=", 1], + [doctype, "is_return", "=", 1], + [child_doctype, reference_field, "=", row_name] + ]) return data[0] diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 111e3940b3..c9f8d0810e 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1257,7 +1257,6 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "per_returned", "fieldtype": "Percent", - "in_list_view": 1, "label": "% Returned", "no_copy": 1, "print_hide": 1, @@ -1268,7 +1267,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-11-19 11:22:09.056684", + "modified": "2020-11-30 12:54:45.407289", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 749b13121d..5bb3095708 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1110,7 +1110,6 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "per_returned", "fieldtype": "Percent", - "in_list_view": 1, "label": "% Returned", "no_copy": 1, "print_hide": 1, @@ -1121,7 +1120,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-11-19 11:21:25.465966", + "modified": "2020-11-30 12:54:23.278500", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 7e619bd59a..97e0fa738c 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -550,19 +550,17 @@ def update_billing_percentage(pr_doc, update_modified=True): # Update Billing % based on pending accepted qty total_amount, total_billed_amount = 0, 0 for item in pr_doc.items: - returned_qty = frappe.db.sql(""" - select sum(abs(child.qty)) as qty - from - `tabPurchase Receipt Item` child, - `tabPurchase Receipt` parent - where - child.parent = parent.name - and parent.docstatus = 1 - and parent.is_return = 1 - and child.purchase_receipt_item = %(row_name)s - """, {"row_name": item.name}) - returned_qty = returned_qty[0][0] if returned_qty else 0 + return_data = frappe.db.get_list("Purchase Receipt", + fields = [ + "sum(abs(`tabPurchase Receipt Item`.qty)) as qty" + ], + filters = [ + ["Purchase Receipt", "docstatus", "=", 1], + ["Purchase Receipt", "is_return", "=", 1], + ["Purchase Receipt Item", "purchase_receipt_item", "=", item.name] + ]) + returned_qty = return_data[0].qty if return_data else 0 returned_amount = flt(returned_qty) * flt(item.rate) pending_amount = flt(item.amount) - returned_amount total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt From a81e872cdb4da0f07179ef5ed5f339c94f51198e Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 12 Nov 2020 16:57:42 +0530 Subject: [PATCH 169/283] fix: added wrong absent days calculation in salary slip --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 4ccf56435d..cecb8cde7c 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -240,7 +240,6 @@ class SalarySlip(TransactionBase): self.absent_days += unmarked_days #will be treated as absent self.payment_days -= unmarked_days if include_holidays_in_total_working_days: - self.absent_days -= len(holidays) for holiday in holidays: if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }): self.payment_days += 1 From 7b16c55c9a387ac5d688a0ee98efd7e93cf12c66 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 13 Nov 2020 16:53:35 +0530 Subject: [PATCH 170/283] feat: Sync old shopify orders (#23841) * feat: Sync old shopify orders * fix: Old orders syncing by date * fix: Remove unnecessary code * fix: Remove unintentional changes Co-authored-by: Saurabh --- .../connectors/shopify_connection.py | 97 ++++++++++++++++--- .../shopify_settings/shopify_settings.json | 78 ++++++++++++++- .../shopify_settings/shopify_settings.py | 12 ++- .../shopify_settings/test_shopify_settings.py | 4 +- erpnext/hooks.py | 1 + erpnext/patches.txt | 1 + .../v13_0/update_custom_fields_for_shopify.py | 10 ++ 7 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 erpnext/patches/v13_0/update_custom_fields_for_shopify.py diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index d59f909298..8aa7453bd6 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -2,12 +2,13 @@ from __future__ import unicode_literals import frappe from frappe import _ import json -from frappe.utils import cstr, cint, nowdate, flt +from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime from erpnext.erpnext_integrations.utils import validate_webhooks_request from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data +from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header @frappe.whitelist(allow_guest=True) @validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret') @@ -18,7 +19,7 @@ def store_request_data(order=None, event=None): dump_request_data(order, event) -def sync_sales_order(order, request_id=None): +def sync_sales_order(order, request_id=None, old_order_sync=False): frappe.set_user('Administrator') shopify_settings = frappe.get_doc("Shopify Settings") frappe.flags.request_id = request_id @@ -27,7 +28,7 @@ def sync_sales_order(order, request_id=None): try: validate_customer(order, shopify_settings) validate_item(order, shopify_settings) - create_order(order, shopify_settings) + create_order(order, shopify_settings, old_order_sync=old_order_sync) except Exception as e: make_shopify_log(status="Error", exception=e) @@ -77,13 +78,13 @@ def validate_item(order, shopify_settings): if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"): sync_item_from_shopify(shopify_settings, item) -def create_order(order, shopify_settings, company=None): +def create_order(order, shopify_settings, old_order_sync=False, company=None): so = create_sales_order(order, shopify_settings, company) if so: if order.get("financial_status") == "paid": - create_sales_invoice(order, shopify_settings, so) + create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync) - if order.get("fulfillments"): + if order.get("fulfillments") and not old_order_sync: create_delivery_note(order, shopify_settings, so) def create_sales_order(shopify_order, shopify_settings, company=None): @@ -92,7 +93,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None): so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name") if not so: - items = get_order_items(shopify_order.get("line_items"), shopify_settings) + items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at'))) if not items: message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' @@ -106,8 +107,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): "doctype": "Sales Order", "naming_series": shopify_settings.sales_order_series or "SO-Shopify-", "shopify_order_id": shopify_order.get("id"), + "shopify_order_number": shopify_order.get("name"), "customer": customer or shopify_settings.default_customer, - "delivery_date": nowdate(), + "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(), + "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(), "company": shopify_settings.company, "selling_price_list": shopify_settings.price_list, "ignore_pricing_rule": 1, @@ -132,12 +135,20 @@ def create_sales_order(shopify_order, shopify_settings, company=None): frappe.db.commit() return so -def create_sales_invoice(shopify_order, shopify_settings, so): +def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False): if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\ and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice): + if old_order_sync: + posting_date = getdate(shopify_order.get('created_at')) + else: + posting_date = nowdate() + si = make_sales_invoice(so.name, ignore_permissions=True) si.shopify_order_id = shopify_order.get("id") + si.shopify_order_number = shopify_order.get("name") + si.set_posting_time = 1 + si.posting_date = posting_date si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" si.flags.ignore_mandatory = True set_cost_center(si.items, shopify_settings.cost_center) @@ -169,6 +180,9 @@ def create_delivery_note(shopify_order, shopify_settings, so): dn = make_delivery_note(so.name) dn.shopify_order_id = fulfillment.get("order_id") + dn.shopify_order_number = shopify_order.get("name") + dn.set_posting_time = 1 + dn.posting_date = getdate(fulfillment.get("created_at")) dn.shopify_fulfillment_id = fulfillment.get("id") dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-" dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings) @@ -187,7 +201,7 @@ def get_discounted_amount(order): discounted_amount += flt(discount.get("amount")) return discounted_amount -def get_order_items(order_items, shopify_settings): +def get_order_items(order_items, shopify_settings, delivery_date): items = [] all_product_exists = True product_not_exists = [] @@ -205,7 +219,7 @@ def get_order_items(order_items, shopify_settings): "item_code": item_code, "item_name": shopify_item.get("name"), "rate": shopify_item.get("price"), - "delivery_date": nowdate(), + "delivery_date": delivery_date, "qty": shopify_item.get("quantity"), "stock_uom": shopify_item.get("uom") or _("Nos"), "warehouse": shopify_settings.warehouse @@ -265,3 +279,64 @@ def get_tax_account_head(tax): frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title"))) return tax_account + +@frappe.whitelist(allow_guest=True) +def sync_old_orders(): + frappe.set_user('Administrator') + shopify_settings = frappe.get_doc('Shopify Settings') + + if not shopify_settings.sync_missing_orders: + return + + url = get_url(shopify_settings) + session = get_request_session() + + try: + res = session.get(url, headers=get_header(shopify_settings)) + res.raise_for_status() + orders = res.json()["orders"] + + for order in orders: + if is_sync_complete(shopify_settings, order): + stop_sync(shopify_settings) + return + + sync_sales_order(order=order, old_order_sync=True) + last_order_id = order.get('id') + + if last_order_id: + shopify_settings.load_from_db() + shopify_settings.last_order_id = last_order_id + shopify_settings.save() + frappe.db.commit() + + except Exception as e: + raise e + +def stop_sync(shopify_settings): + shopify_settings.sync_missing_orders = 0 + shopify_settings.last_order_id = '' + shopify_settings.save() + frappe.db.commit() + +def get_url(shopify_settings): + last_order_id = shopify_settings.last_order_id + + if not last_order_id: + if shopify_settings.sync_based_on == 'Date': + url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format( + get_datetime(shopify_settings.from_date)), shopify_settings) + else: + url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format( + shopify_settings.from_order_id), shopify_settings) + else: + url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings) + + return url + +def is_sync_complete(shopify_settings, order): + if shopify_settings.sync_based_on == 'Date': + return getdate(shopify_settings.to_date) < getdate(order.get('created_at')) + else: + return cstr(order.get('id')) == cstr(shopify_settings.to_order_id) + diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 2e10751f96..20ec06373e 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -1,7 +1,9 @@ { + "actions": [], "creation": "2015-05-18 05:21:07.270859", "doctype": "DocType", "document_type": "System", + "engine": "InnoDB", "field_order": [ "status_html", "enable_shopify", @@ -40,7 +42,16 @@ "sales_invoice_series", "section_break_22", "html_16", - "taxes" + "taxes", + "syncing_details_section", + "sync_missing_orders", + "sync_based_on", + "column_break_41", + "from_date", + "to_date", + "from_order_id", + "to_order_id", + "last_order_id" ], "fields": [ { @@ -255,10 +266,71 @@ "fieldtype": "Table", "label": "Shopify Tax Account", "options": "Shopify Tax Account" + }, + { + "collapsible": 1, + "fieldname": "syncing_details_section", + "fieldtype": "Section Break", + "label": "Syncing Missing Orders" + }, + { + "depends_on": "eval:doc.sync_missing_orders", + "fieldname": "last_order_id", + "fieldtype": "Data", + "label": "Last Order Id", + "read_only": 1 + }, + { + "fieldname": "column_break_41", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "On checking this Order from the ", + "fieldname": "sync_missing_orders", + "fieldtype": "Check", + "label": "Sync Missing Old Shopify Orders" + }, + { + "depends_on": "eval:doc.sync_missing_orders", + "fieldname": "sync_based_on", + "fieldtype": "Select", + "label": "Sync Based On", + "mandatory_depends_on": "eval:doc.sync_missing_orders", + "options": "\nDate\nShopify Order Id" + }, + { + "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" + }, + { + "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" + }, + { + "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", + "fieldname": "from_order_id", + "fieldtype": "Data", + "label": "From Order Id", + "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" + }, + { + "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", + "fieldname": "to_order_id", + "fieldtype": "Data", + "label": "To Order Id", + "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" } ], "issingle": 1, - "modified": "2020-09-18 17:26:09.703215", + "links": [], + "modified": "2020-11-05 20:44:03.664891", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Shopify Settings", @@ -277,4 +349,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 25ffd28109..cbdf90681d 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -87,7 +87,7 @@ def get_shopify_url(path, settings): def get_header(settings): header = {'Content-Type': 'application/json'} - return header; + return header @frappe.whitelist() def get_series(): @@ -121,17 +121,23 @@ def setup_custom_fields(): ], "Sales Order": [ dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1) + fieldtype='Data', insert_after='title', read_only=1, print_hide=1), + dict(fieldname='shopify_order_number', label='Shopify Order Number', + fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) ], "Delivery Note":[ dict(fieldname='shopify_order_id', label='Shopify Order Id', fieldtype='Data', insert_after='title', read_only=1, print_hide=1), + dict(fieldname='shopify_order_number', label='Shopify Order Number', + fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1), dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id', fieldtype='Data', insert_after='title', read_only=1, print_hide=1) ], "Sales Invoice": [ dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1) + fieldtype='Data', insert_after='title', read_only=1, print_hide=1), + dict(fieldname='shopify_order_number', label='Shopify Order Number', + fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) ] } diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py index 64ef3dc085..30fa23cfb4 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py @@ -58,7 +58,7 @@ class ShopifySettings(unittest.TestCase): }).save(ignore_permissions=True) self.shopify_settings = shopify_settings - + def test_order(self): ### Create Customer ### with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer: @@ -75,7 +75,7 @@ class ShopifySettings(unittest.TestCase): with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order: shopify_order = json.load(shopify_order) - create_order(shopify_order.get("order"), self.shopify_settings, "_Test Company") + create_order(shopify_order.get("order"), self.shopify_settings, False, company="_Test Company") sales_order = frappe.get_doc("Sales Order", {"shopify_order_id": cstr(shopify_order.get("order").get("id"))}) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 21dd582593..aadead21ec 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -308,6 +308,7 @@ scheduler_events = { "erpnext.projects.doctype.project.project.collect_project_status", "erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts", "erpnext.support.doctype.issue.issue.set_service_level_agreement_variance", + "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders", ], "daily": [ "erpnext.stock.reorder_item.reorder_item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0d8d1b427a..97177de001 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -734,4 +734,5 @@ erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail erpnext.patches.v13_0.update_reason_for_resignation_in_employee +erpnext.patches.v13_0.update_custom_fields_for_shopify execute:frappe.delete_doc("Report", "Quoted Item Comparison") diff --git a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py new file mode 100644 index 0000000000..f1d2ea2d74 --- /dev/null +++ b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import setup_custom_fields + +def execute(): + if frappe.db.get_single_value('Shopify Settings', 'enable_shopify'): + setup_custom_fields() From d6a2b390dc56ff4e3301e7cc0ca9b300d92084d6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 13 Nov 2020 22:17:07 +0530 Subject: [PATCH 171/283] fix: not able to save bom --- erpnext/manufacturing/doctype/bom/bom.py | 31 +++++++++--------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2ab1b98707..8888a96768 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -76,6 +76,7 @@ class BOM(WebsiteGenerator): self.set_routing_operations() self.validate_operations() self.calculate_cost() + self.update_stock_qty() self.update_cost(update_parent=False, from_child_bom=True, save=False) def get_context(self, context): @@ -84,8 +85,6 @@ class BOM(WebsiteGenerator): def on_update(self): frappe.cache().hdel('bom_children', self.name) self.check_recursion() - self.update_stock_qty() - self.update_exploded_items() def on_submit(self): self.manage_default_bom() @@ -237,7 +236,8 @@ class BOM(WebsiteGenerator): self.calculate_cost() if save: self.db_update() - self.update_exploded_items() + + self.update_exploded_items(save=save) # update parent BOMs if self.total_cost != existing_bom_cost and update_parent: @@ -318,8 +318,6 @@ class BOM(WebsiteGenerator): m.uom = m.stock_uom m.qty = m.stock_qty - m.db_update() - def validate_uom_is_interger(self): from erpnext.utilities.transaction_base import validate_uom_is_integer validate_uom_is_integer(self, "uom", "qty", "BOM Item") @@ -372,15 +370,6 @@ class BOM(WebsiteGenerator): if raise_exception: frappe.throw(_("BOM recursion: {0} cannot be parent or child of {1}").format(self.name, self.name)) - def update_cost_and_exploded_items(self, bom_list=[]): - bom_list = self.traverse_tree(bom_list) - for bom in bom_list: - bom_obj = frappe.get_doc("BOM", bom) - bom_obj.check_recursion(bom_list=bom_list) - bom_obj.update_exploded_items() - - return bom_list - def traverse_tree(self, bom_list=None): def _get_children(bom_no): children = frappe.cache().hget('bom_children', bom_no) @@ -472,10 +461,10 @@ class BOM(WebsiteGenerator): d.rate = rate d.amount = (d.stock_qty or d.qty) * rate - def update_exploded_items(self): + def update_exploded_items(self, save=True): """ Update Flat BOM, following will be correct data""" self.get_exploded_items() - self.add_exploded_items() + self.add_exploded_items(save=save) def get_exploded_items(self): """ Get all raw materials including items from child bom""" @@ -544,11 +533,13 @@ class BOM(WebsiteGenerator): 'sourced_by_supplier': d.get('sourced_by_supplier', 0) })) - def add_exploded_items(self): + def add_exploded_items(self, save=True): "Add items to Flat BOM table" - frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name) self.set('exploded_items', []) + if save: + frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name) + for d in sorted(self.cur_exploded_items, key=itemgetter(0)): ch = self.append('exploded_items', {}) for i in self.cur_exploded_items[d].keys(): @@ -556,7 +547,9 @@ class BOM(WebsiteGenerator): ch.amount = flt(ch.stock_qty) * flt(ch.rate) ch.qty_consumed_per_unit = flt(ch.stock_qty) / flt(self.quantity) ch.docstatus = self.docstatus - ch.db_insert() + + if save: + ch.db_insert() def validate_bom_links(self): if not self.is_active: From ecce3bca2f8237fc3f1a681071dea16b864232b3 Mon Sep 17 00:00:00 2001 From: Marica Date: Sun, 15 Nov 2020 09:17:42 +0530 Subject: [PATCH 172/283] chore: PO cleanup (#23774) * chore: Subcontracting section and items section enhancement - Set target warehouse moved to Items section - Added subcontraction label to supply RM section * chore: PO & PO Item Form Cleanup * chore: PO Get Items from cleanup * fix: Taxes and Charges field visibility * chore: Cleanup * fix: Translation styling --- .../doctype/purchase_order/purchase_order.js | 39 ++++++--- .../purchase_order/purchase_order.json | 81 ++++++++++++------- .../purchase_order_item.json | 56 ++++++------- 3 files changed, 106 insertions(+), 70 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 2f52a9e035..47483c9d1c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -90,6 +90,11 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( this.frm.set_df_property("drop_ship", "hidden", !is_drop_ship); if(doc.docstatus == 1) { + this.frm.fields_dict.items_section.wrapper.addClass("hide-border"); + if(!this.frm.doc.set_warehouse) { + this.frm.fields_dict.items_section.wrapper.removeClass("hide-border"); + } + if(!in_list(["Closed", "Delivered"], doc.status)) { if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) { this.frm.add_custom_button(__('Update Items'), () => { @@ -126,16 +131,25 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if(doc.status != "Closed") { if (doc.status != "On Hold") { if(flt(doc.per_received) < 100 && allow_receipt) { - cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create')); + cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create')); if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) { cur_frm.add_custom_button(__('Material to Supplier'), function() { me.make_stock_entry(); }, __("Transfer")); } } if(flt(doc.per_billed) < 100) - cur_frm.add_custom_button(__('Invoice'), + cur_frm.add_custom_button(__('Purchase Invoice'), this.make_purchase_invoice, __('Create')); + if(flt(doc.per_billed)==0 && doc.status != "Delivered") { + cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create')); + } + + if(flt(doc.per_billed)==0) { + this.frm.add_custom_button(__('Payment Request'), + function() { me.make_payment_request() }, __('Create')); + } + if(!doc.auto_repeat) { cur_frm.add_custom_button(__('Subscription'), function() { erpnext.utils.make_subscription(doc.doctype, doc.name) @@ -156,13 +170,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }); } } - if(flt(doc.per_billed)==0) { - this.frm.add_custom_button(__('Payment Request'), - function() { me.make_payment_request() }, __('Create')); - } - if(flt(doc.per_billed)==0 && doc.status != "Delivered") { - cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create')); - } + cur_frm.page.set_inner_btn_group_as_primary(__('Create')); } } else if(doc.docstatus===0) { @@ -358,12 +366,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order", source_doctype: "Material Request", target: me.frm, - setters: {}, + setters: { + schedule_date: undefined, + status: undefined + }, get_query_filters: { material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], per_ordered: ["<", 99.99], + company: me.frm.doc.company } }) }, __("Get Items From")); @@ -375,16 +387,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( source_doctype: "Supplier Quotation", target: me.frm, setters: { - supplier: me.frm.doc.supplier + supplier: me.frm.doc.supplier, + valid_till: undefined }, get_query_filters: { docstatus: 1, - status: ["!=", "Stopped"], + status: ["not in", ["Stopped", "Expired"]], } }) }, __("Get Items From")); - this.frm.add_custom_button(__('Update rate as per last purchase'), + this.frm.add_custom_button(__('Update Rate as per Last Purchase'), function() { frappe.call({ "method": "get_last_purchase_rate", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 2747c7c54d..4b865a98e9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -30,8 +30,8 @@ "customer_contact_email", "section_addresses", "supplier_address", - "contact_person", "address_display", + "contact_person", "contact_display", "contact_mobile", "contact_email", @@ -49,12 +49,14 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", - "set_warehouse", - "col_break_warehouse", "is_subcontracted", + "col_break_warehouse", "supplier_warehouse", - "items_section", + "before_items_section", "scan_barcode", + "items_col_break", + "set_warehouse", + "items_section", "items", "sb_last_purchase", "total_qty", @@ -108,18 +110,13 @@ "payment_terms_template", "payment_schedule", "tracking_section", - "per_billed", + "status", "column_break_75", + "per_billed", "per_received", "terms_section_break", "tc_name", "terms", - "more_info", - "status", - "ref_sq", - "column_break_74", - "party_account_currency", - "inter_company_order_reference", "column_break5", "letter_head", "select_print_heading", @@ -131,7 +128,12 @@ "to_date", "column_break_97", "auto_repeat", - "update_auto_repeat_reference" + "update_auto_repeat_reference", + "more_info", + "ref_sq", + "column_break_74", + "party_account_currency", + "inter_company_order_reference" ], "fields": [ { @@ -313,34 +315,34 @@ { "fieldname": "supplier_address", "fieldtype": "Link", - "label": "Select Supplier Address", + "label": "Supplier Address", "options": "Address", "print_hide": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", - "label": "Contact Person", + "label": "Supplier Contact", "options": "Contact", "print_hide": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", - "label": "Address", + "label": "Supplier Address Details", "read_only": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, - "label": "Contact", + "label": "Contact Name", "read_only": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", - "label": "Mobile No", + "label": "Contact Mobile No", "read_only": 1 }, { @@ -358,14 +360,14 @@ { "fieldname": "shipping_address", "fieldtype": "Link", - "label": "Select Shipping Address", + "label": "Company Shipping Address", "options": "Address", "print_hide": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", - "label": "Shipping Address", + "label": "Shipping Address Details", "print_hide": 1, "read_only": 1 }, @@ -433,7 +435,8 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Subcontracting" }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -466,6 +469,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -598,7 +602,8 @@ }, { "fieldname": "section_break_52", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes", @@ -626,10 +631,12 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, { + "depends_on": "base_taxes_and_charges_added", "fieldname": "base_taxes_and_charges_added", "fieldtype": "Currency", "label": "Taxes and Charges Added (Company Currency)", @@ -640,6 +647,7 @@ "read_only": 1 }, { + "depends_on": "base_taxes_and_charges_deducted", "fieldname": "base_taxes_and_charges_deducted", "fieldtype": "Currency", "label": "Taxes and Charges Deducted (Company Currency)", @@ -650,6 +658,7 @@ "read_only": 1 }, { + "depends_on": "base_total_taxes_and_charges", "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges (Company Currency)", @@ -665,6 +674,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "taxes_and_charges_added", "fieldname": "taxes_and_charges_added", "fieldtype": "Currency", "label": "Taxes and Charges Added", @@ -675,6 +685,7 @@ "read_only": 1 }, { + "depends_on": "taxes_and_charges_deducted", "fieldname": "taxes_and_charges_deducted", "fieldtype": "Currency", "label": "Taxes and Charges Deducted", @@ -685,6 +696,7 @@ "read_only": 1 }, { + "depends_on": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", @@ -694,7 +706,7 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", + "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", "label": "Additional Discount" @@ -734,7 +746,8 @@ }, { "fieldname": "totals_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -902,12 +915,12 @@ }, { "fieldname": "ref_sq", - "fieldtype": "Data", - "hidden": 1, - "label": "Ref SQ", + "fieldtype": "Link", + "label": "Supplier Quotation", "no_copy": 1, "oldfieldname": "ref_sq", "oldfieldtype": "Data", + "options": "Supplier Quotation", "print_hide": 1, "read_only": 1 }, @@ -1061,7 +1074,7 @@ "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking" + "label": "Order Status" }, { "fieldname": "column_break_75", @@ -1070,21 +1083,29 @@ { "fieldname": "billing_address", "fieldtype": "Link", - "label": "Select Billing Address", + "label": "Company Billing Address", "options": "Address" }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", - "label": "Billing Address", + "label": "Billing Address Details", "read_only": 1 + }, + { + "fieldname": "before_items_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "items_col_break", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-10-07 14:31:57.661221", + "modified": "2020-10-30 11:39:37.388249", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 7a52c28a0e..10db240a44 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -24,6 +24,7 @@ "col_break2", "uom", "conversion_factor", + "stock_qty", "sec_break1", "price_list_rate", "discount_percentage", @@ -46,11 +47,8 @@ "column_break_32", "base_net_rate", "base_net_amount", - "billed_amt", "warehouse_and_reference", "warehouse", - "delivered_by_supplier", - "project", "material_request", "material_request_item", "sales_order", @@ -58,36 +56,37 @@ "supplier_quotation", "supplier_quotation_item", "col_break5", + "delivered_by_supplier", "against_blanket_order", "blanket_order", "blanket_order_rate", "item_group", "brand", - "bom", - "include_exploded_items", "section_break_56", - "stock_qty", - "column_break_60", "received_qty", "returned_qty", - "manufacture_details", - "manufacturer", - "column_break_14", - "manufacturer_part_no", - "more_info_section_break", - "is_fixed_asset", - "item_tax_rate", + "column_break_60", + "billed_amt", "accounting_details", "expense_account", - "column_break_68", + "manufacture_details", + "manufacturer", + "manufacturer_part_no", + "column_break_14", + "bom", + "include_exploded_items", "item_weight_details", "weight_per_unit", "total_weight", "column_break_40", "weight_uom", "accounting_dimensions_section", - "cost_center", + "project", "dimension_col_break", + "cost_center", + "more_info_section_break", + "is_fixed_asset", + "item_tax_rate", "section_break_72", "page_break" ], @@ -346,6 +345,7 @@ }, { "default": "0", + "depends_on": "is_free_item", "fieldname": "is_free_item", "fieldtype": "Check", "label": "Is Free Item", @@ -508,9 +508,10 @@ }, { "default": "0", + "depends_on": "delivered_by_supplier", "fieldname": "delivered_by_supplier", "fieldtype": "Check", - "label": "To be delivered to customer", + "label": "To be Delivered to Customer", "print_hide": 1, "read_only": 1 }, @@ -558,6 +559,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.is_subcontracted == 'Yes'", "fieldname": "bom", "fieldtype": "Link", "label": "BOM", @@ -574,21 +576,21 @@ }, { "fieldname": "section_break_56", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Billed, Received & Returned" }, { "fieldname": "stock_qty", "fieldtype": "Float", - "label": "Qty as per Stock UOM", + "label": "Qty in Stock UOM", "no_copy": 1, - "oldfieldname": "stock_qty", - "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", "read_only": 1, "width": "100px" }, { + "depends_on": "received_qty", "fieldname": "received_qty", "fieldtype": "Float", "label": "Received Qty", @@ -612,9 +614,10 @@ "fieldtype": "Column Break" }, { + "depends_on": "billed_amt", "fieldname": "billed_amt", "fieldtype": "Currency", - "label": "Billed Amt", + "label": "Billed Amount", "no_copy": 1, "options": "currency", "print_hide": 1, @@ -633,6 +636,7 @@ "report_hide": 1 }, { + "collapsible": 1, "fieldname": "accounting_details", "fieldtype": "Section Break", "label": "Accounting Details" @@ -644,10 +648,6 @@ "options": "Account", "print_hide": 1 }, - { - "fieldname": "column_break_68", - "fieldtype": "Column Break" - }, { "fieldname": "cost_center", "fieldtype": "Link", @@ -715,6 +715,7 @@ }, { "default": "0", + "depends_on": "is_fixed_asset", "fetch_from": "item_code.is_fixed_asset", "fieldname": "is_fixed_asset", "fieldtype": "Check", @@ -728,9 +729,10 @@ } ], "idx": 1, + "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-04-21 11:55:58.643393", + "modified": "2020-10-30 11:59:47.670951", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", From 1ea12079da89921470ec5f8e423630dd3dded71a Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 15 Nov 2020 05:02:24 +0100 Subject: [PATCH 173/283] fix: fiscal year can be shorter than 12 months (#23838) * fix: replace unnecessary SQL query * feat: checkbox "Is Short Year" Also, set checkbox and dates only once. * fix: toggle no longer necessary Date fields use set_only_once. * feat: short years can be less than 12 months * fix: if short year, don't add 12 months to start date * test: short year * fix: move short fiscal year to test records --- .../doctype/fiscal_year/fiscal_year.js | 14 +- .../doctype/fiscal_year/fiscal_year.json | 435 +++++------------- .../doctype/fiscal_year/fiscal_year.py | 19 +- .../doctype/fiscal_year/test_fiscal_year.py | 1 + .../doctype/fiscal_year/test_records.json | 7 + 5 files changed, 131 insertions(+), 345 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index 152e17dbc8..bc77dac1cd 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -9,11 +9,7 @@ frappe.ui.form.on('Fiscal Year', { } }, refresh: function (frm) { - let doc = frm.doc; - frm.toggle_enable('year_start_date', doc.__islocal); - frm.toggle_enable('year_end_date', doc.__islocal); - - if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) { + if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) { frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm)); frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); } else { @@ -24,8 +20,10 @@ frappe.ui.form.on('Fiscal Year', { return frm.call('set_as_default'); }, year_start_date: function(frm) { - let year_end_date = - frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1); - frm.set_value("year_end_date", year_end_date); + if (!frm.doc.is_short_year) { + let year_end_date = + frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1); + frm.set_value("year_end_date", year_end_date); + } }, }); diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index 4ca9f6b96f..5ab91f2506 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -1,347 +1,126 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "field:year", - "beta": 0, - "creation": "2013-01-22 16:50:25", - "custom": 0, - "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "autoname": "field:year", + "creation": "2013-01-22 16:50:25", + "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "year", + "disabled", + "is_short_year", + "year_start_date", + "year_end_date", + "companies", + "auto_created" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For e.g. 2012, 2012-13", - "fieldname": "year", - "fieldtype": "Data", - "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": "Year Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "year", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "For e.g. 2012, 2012-13", + "fieldname": "year", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Year Name", + "oldfieldname": "year", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "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": "Disabled", - "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": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "year_start_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": "Year Start Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "year_start_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "year_start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Year Start Date", + "no_copy": 1, + "oldfieldname": "year_start_date", + "oldfieldtype": "Date", + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "year_end_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": "Year End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "year_end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Year End Date", + "no_copy": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "companies", - "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": "Companies", - "length": 0, - "no_copy": 0, - "options": "Fiscal Year 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "companies", + "fieldtype": "Table", + "label": "Companies", + "options": "Fiscal Year Company" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "auto_created", - "fieldtype": "Check", - "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": "Auto Created", - "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": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "auto_created", + "fieldtype": "Check", + "hidden": 1, + "label": "Auto Created", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "description": "Less than 12 months.", + "fieldname": "is_short_year", + "fieldtype": "Check", + "label": "Is Short Year", + "set_only_once": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-calendar", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-25 14:21:41.273354", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Fiscal Year", - "owner": "Administrator", + ], + "icon": "fa fa-calendar", + "idx": 1, + "links": [], + "modified": "2020-11-05 12:16:53.081573", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Fiscal Year", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Sales User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Purchase User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Accounts User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "role": "Stock User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "Employee" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "name", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "name", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index d80bc7fad1..da6a3fd2ef 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -36,6 +36,11 @@ class FiscalYear(Document): frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved.")) def validate_dates(self): + if self.is_short_year: + # Fiscal Year can be shorter than one year, in some jurisdictions + # under certain circumstances. For example, in the USA and Germany. + return + if getdate(self.year_start_date) > getdate(self.year_end_date): frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"), FiscalYearIncorrectDate) @@ -116,12 +121,8 @@ def auto_create_fiscal_year(): pass def get_from_and_to_date(fiscal_year): - from_and_to_date_tuple = frappe.db.sql("""select year_start_date, year_end_date - from `tabFiscal Year` where name=%s""", (fiscal_year))[0] - - from_and_to_date = { - "from_date": from_and_to_date_tuple[0], - "to_date": from_and_to_date_tuple[1] - } - - return from_and_to_date + fields = [ + "year_start_date as from_date", + "year_end_date as to_date" + ] + return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1) diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index f7b7782766..cec4f4492d 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -11,6 +11,7 @@ test_records = frappe.get_test_records('Fiscal Year') test_ignore = ["Company"] class TestFiscalYear(unittest.TestCase): + def test_extra_year(self): if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"): frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000") diff --git a/erpnext/accounts/doctype/fiscal_year/test_records.json b/erpnext/accounts/doctype/fiscal_year/test_records.json index d5723ca62b..44052535cb 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_records.json +++ b/erpnext/accounts/doctype/fiscal_year/test_records.json @@ -1,4 +1,11 @@ [ + { + "doctype": "Fiscal Year", + "year": "_Test Short Fiscal Year 2011", + "is_short_year": 1, + "year_end_date": "2011-04-01", + "year_start_date": "2011-12-31" + }, { "doctype": "Fiscal Year", "year": "_Test Fiscal Year 2012", From 59be7ff144bd4e17111732fe526d719140653d4c Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 15 Nov 2020 05:04:05 +0100 Subject: [PATCH 174/283] feat: make account number length configurable (#23845) --- .../datev_settings/datev_settings.json | 22 ++++++++++++++----- .../regional/germany/utils/datev/datev_csv.py | 2 +- erpnext/regional/report/datev/datev.py | 2 ++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json index 39486dfc12..713e8e34ef 100644 --- a/erpnext/regional/doctype/datev_settings/datev_settings.json +++ b/erpnext/regional/doctype/datev_settings/datev_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "field:client", "creation": "2019-08-13 23:56:34.259906", "doctype": "DocType", @@ -6,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "client", + "account_number_length", "column_break_2", "client_number", "section_break_4", @@ -28,8 +30,8 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Client ID", - "reqd": 1, - "length": 5 + "length": 5, + "reqd": 1 }, { "fieldname": "consultant", @@ -43,8 +45,8 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Consultant ID", - "reqd": 1, - "length": 7 + "length": 7, + "reqd": 1 }, { "fieldname": "column_break_2", @@ -57,9 +59,17 @@ { "fieldname": "column_break_6", "fieldtype": "Column Break" + }, + { + "default": "4", + "fieldname": "account_number_length", + "fieldtype": "Int", + "label": "Account Number Length", + "reqd": 1 } ], - "modified": "2019-08-14 00:03:26.616460", + "links": [], + "modified": "2020-11-05 17:52:11.674329", "modified_by": "Administrator", "module": "Regional", "name": "DATEV Settings", @@ -104,4 +114,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py index cf07a1c824..f138a807bc 100644 --- a/erpnext/regional/germany/utils/datev/datev_csv.py +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -106,7 +106,7 @@ def get_header(filters, csv_class): # M = Start of the fiscal year (Wirtschaftsjahresbeginn) frappe.utils.formatdate(filters.get('fiscal_year_start'), 'yyyyMMdd'), # N = Length of account numbers (Sachkontenlänge) - datev_settings.get('account_number_length', '4'), + str(filters.get('account_number_length', 4)), # O = Transaction batch start date (YYYYMMDD) frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # P = Transaction batch end date (YYYYMMDD) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index dbae230f1e..3f4cb981cc 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -340,6 +340,8 @@ def download_datev_csv(filters): coa = frappe.get_value('Company', company, 'chart_of_accounts') filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') + filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length') + transactions = get_transactions(filters) account_names = get_account_names(filters) customers = get_customers(filters) From 10c168e2578ad96afdd2b6d4fecd492fe1e0ee46 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 15 Nov 2020 09:40:44 +0530 Subject: [PATCH 175/283] fix: default cost center in item master not set in stock entry (#23877) Co-authored-by: Marica --- .../doctype/work_order/test_work_order.py | 5 ++++ .../stock/doctype/stock_entry/stock_entry.py | 5 ++-- erpnext/stock/get_item_details.py | 23 ++++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 5f8a13428c..e53927918e 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -443,6 +443,11 @@ class TestWorkOrder(unittest.TestCase): ste1 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1)) self.assertEqual(len(ste1.items), 3) + def test_cost_center_for_manufacture(self): + wo_order = make_wo_order_test_record() + ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty) + self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC") + def test_operation_time_with_batch_size(self): fg_item = "Test Batch Size Item For BOM" rm1 = "Test Batch Size Item RM 1 For BOM" diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 768526705c..e3159b95c3 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1227,8 +1227,6 @@ class StockEntry(StockController): return item_dict def add_to_stock_entry_detail(self, item_dict, bom_no=None): - cost_center = frappe.db.get_value("Company", self.company, 'cost_center') - for d in item_dict: stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom") @@ -1239,9 +1237,10 @@ class StockEntry(StockController): se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom se_child.stock_uom = stock_uom se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty")) - se_child.cost_center = item_dict[d].get("cost_center") or cost_center se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) se_child.subcontracted_item = item_dict[d].get("main_item_code") + se_child.cost_center = (item_dict[d].get("cost_center") or + get_default_cost_center(item_dict[d], company = self.company)) for field in ["idx", "po_detail", "original_item", "expense_account", "description", "item_name"]: diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 8d8dcb74c3..08f7a83b89 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -559,23 +559,40 @@ def get_default_deferred_account(args, item, fieldname=None): else: return None -def get_default_cost_center(args, item, item_group, brand, company=None): +def get_default_cost_center(args, item=None, item_group=None, brand=None, company=None): cost_center = None + + if not company and args.get("company"): + company = args.get("company") + if args.get('project'): cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True) - if not cost_center: + if not cost_center and (item and item_group and brand): if args.get('customer'): cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center') else: cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center') - cost_center = cost_center or args.get("cost_center") + elif not cost_center and args.get("item_code") and company: + for method in ["get_item_defaults", "get_item_group_defaults", "get_brand_defaults"]: + path = "erpnext.stock.get_item_details.{0}".format(method) + data = frappe.get_attr(path)(args.get("item_code"), company) + + if data and (data.selling_cost_center or data.buying_cost_center): + return data.selling_cost_center or data.buying_cost_center + + if not cost_center and args.get("cost_center"): + cost_center = args.get("cost_center") if (company and cost_center and frappe.get_cached_value("Cost Center", cost_center, "company") != company): return None + if not cost_center and company: + cost_center = frappe.get_cached_value("Company", + company, "cost_center") + return cost_center def get_default_supplier(args, item, item_group, brand): From 39102e68dfa76050ea33a5b4cda3f7cb4aac615c Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Sun, 15 Nov 2020 09:44:36 +0530 Subject: [PATCH 176/283] fix: make contract template editable (#23891) --- erpnext/crm/doctype/contract_template/contract_template.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index ef9974f863..5e4582f8d3 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -23,8 +23,7 @@ { "fieldname": "contract_terms", "fieldtype": "Text Editor", - "label": "Contract Terms and Conditions", - "read_only": 1 + "label": "Contract Terms and Conditions" }, { "fieldname": "sb_fulfilment", @@ -45,7 +44,7 @@ } ], "links": [], - "modified": "2020-06-03 00:24:58.179816", + "modified": "2020-11-11 17:49:44.879363", "modified_by": "Administrator", "module": "CRM", "name": "Contract Template", From 896e4b1722dcf3e1bcab13d1421b53efec44e92e Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 15 Nov 2020 05:15:10 +0100 Subject: [PATCH 177/283] fix: improve UX of DATEV report (#23892) * feat(DATEV Settings): button to show report * feat(desk): add DATEV to Reports under Accounting * fix(report DATEV): last calendar month as default * fix: let user create DATEV Settings (Instead of showing an error message.) --- .../desk_page/accounting/accounting.json | 4 ++-- .../doctype/datev_settings/datev_settings.js | 6 +++--- erpnext/regional/report/datev/datev.js | 18 ++++++++++++++++-- erpnext/regional/report/datev/datev.py | 19 +++++++++++++------ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 45e3dcf5e7..2917a36463 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\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 \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\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 \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -99,7 +99,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-11-06 13:05:58.650150", + "modified": "2020-11-11 18:35:11.542909", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js index 69747b0b89..f04705929f 100644 --- a/erpnext/regional/doctype/datev_settings/datev_settings.js +++ b/erpnext/regional/doctype/datev_settings/datev_settings.js @@ -2,7 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on('DATEV Settings', { - // refresh: function(frm) { - - // } + refresh: function(frm) { + frm.add_custom_button('Show Report', () => frappe.set_route('query-report', 'DATEV'), "fa fa-table"); + } }); diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js index 55f12cf373..4124e3df19 100644 --- a/erpnext/regional/report/datev/datev.js +++ b/erpnext/regional/report/datev/datev.js @@ -11,14 +11,14 @@ frappe.query_reports["DATEV"] = { { "fieldname": "from_date", "label": __("From Date"), - "default": frappe.datetime.month_start(), + "default": moment().subtract(1, 'month').startOf('month').format(), "fieldtype": "Date", "reqd": 1 }, { "fieldname": "to_date", "label": __("To Date"), - "default": frappe.datetime.now_date(), + "default": moment().subtract(1, 'month').endOf('month').format(), "fieldtype": "Date", "reqd": 1 }, @@ -30,9 +30,23 @@ frappe.query_reports["DATEV"] = { } ], onload: function(query_report) { + let company = frappe.query_report.get_filter_value('company'); + frappe.db.exists('DATEV Settings', company).then((settings_exist) => { + if (!settings_exist) { + frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'), + () => frappe.new_doc('DATEV Settings', {'company': company}) + ); + } + }); + query_report.page.add_menu_item(__("Download DATEV File"), () => { const filters = JSON.stringify(query_report.get_values()); window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`); }); + + query_report.page.add_menu_item(__("Change DATEV Settings"), () => { + let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now. + frappe.set_route('Form', 'DATEV Settings', company); + }); } }; diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 3f4cb981cc..1e39c57786 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -94,8 +94,11 @@ COLUMNS = [ def execute(filters=None): """Entry point for frappe.""" - validate(filters) - return COLUMNS, get_transactions(filters, as_dict=0) + data = [] + if filters and validate(filters): + data = get_transactions(filters, as_dict=0) + + return COLUMNS, data def validate(filters): @@ -114,10 +117,14 @@ def validate(filters): validate_fiscal_year(from_date, to_date, company) - try: - frappe.get_doc('DATEV Settings', filters.get('company')) - except frappe.DoesNotExistError: - frappe.throw(_('Please create DATEV Settings for Company {}.').format(filters.get('company'))) + if not frappe.db.exists('DATEV Settings', filters.get('company')): + frappe.log_error(_('Please create {} for Company {}.').format( + '{}'.format(_('DATEV Settings')), + frappe.bold(filters.get('company')) + )) + return False + + return True def validate_fiscal_year(from_date, to_date, company): From 58a3fea1b7229378713a1b1f95a9d84d71a9aeeb Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sun, 15 Nov 2020 11:14:35 +0530 Subject: [PATCH 178/283] fix: Error handling in Upload Attendance (#23907) --- erpnext/hr/doctype/upload_attendance/upload_attendance.js | 8 ++++---- erpnext/hr/doctype/upload_attendance/upload_attendance.py | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.js b/erpnext/hr/doctype/upload_attendance/upload_attendance.js index 9df2948a15..29aa85484a 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.js +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.js @@ -24,10 +24,10 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({ } window.location.href = repl(frappe.request.url + '?cmd=%(cmd)s&from_date=%(from_date)s&to_date=%(to_date)s', { - cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template", - from_date: this.frm.doc.att_fr_date, - to_date: this.frm.doc.att_to_date, - }); + cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template", + from_date: this.frm.doc.att_fr_date, + to_date: this.frm.doc.att_to_date, + }); }, show_upload() { diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index edf05e827b..674c8e3eb4 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -28,7 +28,12 @@ def get_template(): w = UnicodeWriter() w = add_header(w) - w = add_data(w, args) + try: + w = add_data(w, args) + except Exception as e: + frappe.clear_messages() + frappe.respond_as_web_page("Holiday List Missing", html=e) + return # write out response as a type csv frappe.response['result'] = cstr(w.getvalue()) From 8e2ce641c3d96d6c126e47bc9c691d8ff4671024 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 14 Oct 2020 14:05:54 +0530 Subject: [PATCH 179/283] fix: Dont overrule Item Price via Pricing Rule Rate if 0 --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 149c47673c..55a5b0e513 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -352,8 +352,14 @@ def apply_price_discount_rule(pricing_rule, item_details, args): pricing_rule_rate = 0.0 if pricing_rule.currency == args.currency: pricing_rule_rate = pricing_rule.rate + + if pricing_rule_rate: + # Override already set price list rate (from item price) + # if pricing_rule_rate > 0 + item_details.update({ + "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1), + }) item_details.update({ - "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1), "discount_percentage": 0.0 }) From d0ee615c2ce33c9cc0693877974f8b80e0b51ad3 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 3 Nov 2020 20:22:48 +0530 Subject: [PATCH 180/283] chore: Pricing Rule with Item Price Test --- .../doctype/pricing_rule/test_pricing_rule.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 22a031c162..ec0a485bfc 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -484,6 +484,43 @@ class TestPricingRule(unittest.TestCase): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + def test_item_price_with_pricing_rule(self): + item = make_item("Water Flask") + make_item_price("Water Flask", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Water Flask Rule", + "apply_on": "Item Code", + "items": [{ + "item_code": "Water Flask", + }], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 0, + "margin_type": "Percentage", + "margin_rate_or_amount": 2, + "company": "_Test Company" + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice(do_not_save=True, item_code="Water Flask") + si.selling_price_list = "_Test Price List" + si.save() + + # If rate in Rule is 0, give preference to Item Price if it exists + self.assertEqual(si.items[0].price_list_rate, 100) + self.assertEqual(si.items[0].margin_rate_or_amount, 2) + self.assertEqual(si.items[0].rate_with_margin, 102) + self.assertEqual(si.items[0].rate, 102) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete() + item.delete() + def make_pricing_rule(**args): args = frappe._dict(args) From beb1811523b4ed57a2978a8632507ec96eff4947 Mon Sep 17 00:00:00 2001 From: Afshan Date: Wed, 11 Nov 2020 15:13:38 +0530 Subject: [PATCH 181/283] fix: payroll attendance error --- .../doctype/payroll_entry/payroll_entry.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 30ea432678..49c204ab44 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -344,9 +344,13 @@ class PayrollEntry(Document): employees_to_mark_attendance = [] days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0 for employee_detail in self.employees: - days_holiday = self.get_count_holidays_of_employee(employee_detail.employee) - days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee) - days_in_payroll = date_diff(self.end_date, self.start_date) + 1 + employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining') + start_date = self.start_date + if employee_joining_date > getdate(self.start_date): + start_date = employee_joining_date + days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date) + days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date) + days_in_payroll = date_diff(self.end_date, start_date) + 1 if days_in_payroll > days_holiday + days_attendance_marked: employees_to_mark_attendance.append({ "employee": employee_detail.employee, @@ -354,22 +358,22 @@ class PayrollEntry(Document): }) return employees_to_mark_attendance - def get_count_holidays_of_employee(self, employee): + def get_count_holidays_of_employee(self, employee, start_date): holiday_list = get_holiday_list_for_employee(employee) holidays = 0 if holiday_list: days = frappe.db.sql("""select count(*) from tabHoliday where parent=%s and holiday_date between %s and %s""", (holiday_list, - self.start_date, self.end_date)) + start_date, self.end_date)) if days and days[0][0]: holidays = days[0][0] return holidays - def get_count_employee_attendance(self, employee): + def get_count_employee_attendance(self, employee, start_date): marked_days = 0 attendances = frappe.db.sql("""select count(*) from tabAttendance where employee=%s and docstatus=1 and attendance_date between %s and %s""", - (employee, self.start_date, self.end_date)) + (employee, start_date, self.end_date)) if attendances and attendances[0][0]: marked_days = attendances[0][0] return marked_days From 8c3b2d0a0d04c1d7e021b4462986e5645534e01c Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 13 Nov 2020 17:04:05 +0530 Subject: [PATCH 182/283] fix: change query to frappe.get_all --- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 49c204ab44..a3d12c35c0 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -371,9 +371,12 @@ class PayrollEntry(Document): def get_count_employee_attendance(self, employee, start_date): marked_days = 0 - attendances = frappe.db.sql("""select count(*) from tabAttendance where - employee=%s and docstatus=1 and attendance_date between %s and %s""", - (employee, start_date, self.end_date)) + attendances = frappe.get_all("Attendance", + fields = ["count(*)"], + filters = { + "employee": employee, + "attendance_date": ('between', [start_date, self.end_date]) + }, as_list=1) if attendances and attendances[0][0]: marked_days = attendances[0][0] return marked_days From c8b5f982ac0f97f88b41e1c2f22d99a2d21f3f37 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 17 Nov 2020 10:58:09 +0530 Subject: [PATCH 183/283] fix: Typo (Enchashment > Encashment) (#23919) --- erpnext/patches/v12_0/generate_leave_ledger_entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 7afde373c3..fe072d7eb9 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -51,7 +51,7 @@ def generate_encashment_leave_ledger_entries(): for encashment in leave_encashments: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}): - frappe.get_doc("Leave Enchashment", encashment).create_leave_ledger_entry() + frappe.get_doc("Leave Encashment", encashment).create_leave_ledger_entry() def generate_expiry_allocation_ledger_entries(): ''' fix ledger entries for missing leave allocation transaction ''' From 1466ed4579736422e0ded807a044c05384a1d460 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 17 Nov 2020 11:12:31 +0530 Subject: [PATCH 184/283] fix: Don't copy terms and discount from SO to PO (#23903) * fix: Dont copy terms, discount and required by from SO to PO * fix: Let delivery date and required by date get mapped --- .../selling/doctype/sales_order/sales_order.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index ec1c82339b..04d85e575c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -844,7 +844,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "contact_email", "contact_person", "taxes_and_charges", - "shipping_address" + "shipping_address", + "terms" ], "validation": { "docstatus": ["=", 1] @@ -863,7 +864,10 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "field_no_map": [ "rate", "price_list_rate", - "item_tax_template" + "item_tax_template", + "discount_percentage", + "discount_amount", + "pricing_rules" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map @@ -917,7 +921,8 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): "contact_email", "contact_person", "taxes_and_charges", - "shipping_address" + "shipping_address", + "terms" ], "validation": { "docstatus": ["=", 1] @@ -937,7 +942,10 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): "rate", "price_list_rate", "item_tax_template", - "supplier" + "discount_percentage", + "discount_amount", + "supplier", + "pricing_rules" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.item_code in items_to_map From 7e2d8ca91676c9edafcfa7e8378ae0a2ac109052 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 17 Nov 2020 12:08:31 +0530 Subject: [PATCH 185/283] fix: Handle the "no leave_allocation found" case (#23922) * fix: Handle the "no leave_allocation found" case * fix: format of error msg --- erpnext/hr/doctype/leave_encashment/leave_encashment.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 8913c648c5..c1dcc97b1a 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -32,7 +32,7 @@ class LeaveEncashment(Document): additional_salary.employee = self.employee earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") if not earning_component: - frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type))) + frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type)) additional_salary.salary_component = earning_component additional_salary.payroll_date = self.encashment_date additional_salary.amount = self.encashment_amount @@ -98,7 +98,11 @@ class LeaveEncashment(Document): create_leave_ledger_entry(self, args, submit) # create reverse entry for expired leaves - to_date = self.get_leave_allocation().get('to_date') + leave_allocation = self.get_leave_allocation() + if not leave_allocation: + return + + to_date = leave_allocation.get('to_date') if to_date < getdate(nowdate()): args = frappe._dict( leaves=self.encashable_days, From 9707b4789e24071752660d3f55b22d118fd3f143 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 17 Nov 2020 12:10:27 +0530 Subject: [PATCH 186/283] fix: stock ageing report not working (#23923) --- erpnext/stock/report/stock_ageing/stock_ageing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 3dc806fb43..8aaf7abcbe 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -20,7 +20,8 @@ def execute(filters=None): fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func) details = item_dict["details"] - if not fifo_queue and (not item_dict.get("total_qty")): continue + + if not fifo_queue: continue average_age = get_average_age(fifo_queue, to_date) earliest_age = date_diff(to_date, fifo_queue[0][1]) From 61388d412b11f7fbcab52871c147695f0e01ef35 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 17 Nov 2020 13:02:15 +0530 Subject: [PATCH 187/283] feat: update desk pages --- .../desk_page/accounting/accounting.json | 953 +++++++++++++++++- .../desk_page/agriculture/agriculture.json | 124 ++- erpnext/assets/desk_page/assets/assets.json | 133 ++- erpnext/buying/desk_page/buying/buying.json | 413 +++++++- erpnext/crm/desk_page/crm/crm.json | 318 +++++- .../desk_page/education/education.json | 569 ++++++++++- .../erpnext_integrations.json | 88 +- .../erpnext_integrations_settings.json | 56 +- .../desk_page/healthcare/healthcare.json | 427 +++++++- erpnext/hr/desk_page/hr/hr.json | 804 ++++++++++++++- .../loan_management/desk_page/loan/loan.json | 192 +++- .../manufacturing/manufacturing.json | 252 ++++- .../desk_page/non_profit/non_profit.json | 163 ++- .../payroll/desk_page/payroll/payroll.json | 248 ++++- .../projects/desk_page/projects/projects.json | 124 ++- .../desk_page/quality/quality.json | 122 ++- erpnext/selling/desk_page/retail/retail.json | 79 +- .../selling/desk_page/selling/selling.json | 453 ++++++++- .../erpnext_settings/erpnext_settings.json | 3 +- erpnext/setup/desk_page/home/home.json | 373 ++++++- erpnext/stock/desk_page/stock/stock.json | 582 ++++++++++- .../support/desk_page/support/support.json | 136 ++- .../desk_page/utilities/utilities.json | 31 +- 23 files changed, 6601 insertions(+), 42 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 2917a36463..dfc2243b38 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -99,7 +99,958 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-11-11 18:35:11.542909", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Masters", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Company", + "link_to": "Company", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Accounts", + "link_to": "Account", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Settings", + "link_to": "Accounts Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fiscal Year", + "link_to": "Fiscal Year", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Dimension", + "link_to": "Accounting Dimension", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Finance Book", + "link_to": "Finance Book", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Period", + "link_to": "Accounting Period", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Term", + "link_to": "Payment Term", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "General Ledger", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Journal Entry", + "link_to": "Journal Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Journal Entry Template", + "link_to": "Journal Entry Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "General Ledger", + "link_to": "General Ledger", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customer Ledger Summary", + "link_to": "Customer Ledger Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Supplier Ledger Summary", + "link_to": "Supplier Ledger Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Receivable", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Entry", + "link_to": "Payment Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Request", + "link_to": "Payment Request", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Accounts Receivable", + "link_to": "Accounts Receivable", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Accounts Receivable Summary", + "link_to": "Accounts Receivable Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Register", + "link_to": "Sales Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item-wise Sales Register", + "link_to": "Item-wise Sales Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Delivered Items To Be Billed", + "link_to": "Delivered Items To Be Billed", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounts Payable", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier", + "link_to": "Supplier", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Entry", + "link_to": "Payment Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Accounts Payable", + "link_to": "Accounts Payable", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Accounts Payable Summary", + "link_to": "Accounts Payable Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Register", + "link_to": "Purchase Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Received Items To Be Billed", + "link_to": "Received Items To Be Billed", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Trial Balance for Party", + "link_to": "Trial Balance for Party", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Payment Period Based On Invoice Date", + "link_to": "Payment Period Based On Invoice Date", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Partners Commission", + "link_to": "Sales Partners Commission", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customer Credit Balance", + "link_to": "Customer Credit Balance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Payment Summary", + "link_to": "Sales Payment Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Address And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "DATEV Export", + "link_to": "DATEV", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Financial Statements", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Trial Balance", + "link_to": "Trial Balance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Profit and Loss Statement", + "link_to": "Profit and Loss Statement", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Balance Sheet", + "link_to": "Balance Sheet", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Cash Flow", + "link_to": "Cash Flow", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Consolidated Financial Statement", + "link_to": "Consolidated Financial Statement", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Multi Currency", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Currency", + "link_to": "Currency", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Currency Exchange", + "link_to": "Currency Exchange", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exchange Rate Revaluation", + "link_to": "Exchange Rate Revaluation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payment Gateway Account", + "link_to": "Payment Gateway Account", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Terms and Conditions Template", + "link_to": "Terms and Conditions", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Mode of Payment", + "link_to": "Mode of Payment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Statement", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank", + "link_to": "Bank", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Account", + "link_to": "Bank Account", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Clearance", + "link_to": "Bank Clearance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Reconciliation", + "link_to": "bank-reconciliation", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Bank Reconciliation Statement", + "link_to": "Bank Reconciliation Statement", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Statement Transaction Entry", + "link_to": "Bank Statement Transaction Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bank Statement Settings", + "link_to": "Bank Statement Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Subscription Management", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Subscription Plan", + "link_to": "Subscription Plan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Subscription", + "link_to": "Subscription", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Subscription Settings", + "link_to": "Subscription Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Goods and Services Tax (GST India)", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "GST Settings", + "link_to": "GST Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "GST HSN Code", + "link_to": "GST HSN Code", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GSTR-1", + "link_to": "GSTR-1", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GSTR-2", + "link_to": "GSTR-2", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "GSTR 3B Report", + "link_to": "GSTR 3B Report", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GST Sales Register", + "link_to": "GST Sales Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GST Purchase Register", + "link_to": "GST Purchase Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GST Itemised Sales Register", + "link_to": "GST Itemised Sales Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "GST Itemised Purchase Register", + "link_to": "GST Itemised Purchase Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "C-Form", + "link_to": "C-Form", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lower Deduction Certificate", + "link_to": "Lower Deduction Certificate", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Share Management", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shareholder", + "link_to": "Shareholder", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Share Transfer", + "link_to": "Share Transfer", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Share Ledger", + "link_to": "Share Ledger", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Share Balance", + "link_to": "Share Balance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Cost Center and Budgeting", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Cost Centers", + "link_to": "Cost Center", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Budget", + "link_to": "Budget", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting Dimension", + "link_to": "Accounting Dimension", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Budget Variance Report", + "link_to": "Budget Variance Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Monthly Distribution", + "link_to": "Monthly Distribution", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Opening and Closing", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Opening Invoice Creation Tool", + "link_to": "Opening Invoice Creation Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Accounts Importer", + "link_to": "Chart of Accounts Importer", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Period Closing Voucher", + "link_to": "Period Closing Voucher", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Taxes", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Taxes and Charges Template", + "link_to": "Sales Taxes and Charges Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Taxes and Charges Template", + "link_to": "Purchase Taxes and Charges Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Tax Template", + "link_to": "Item Tax Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tax Category", + "link_to": "Tax Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tax Rule", + "link_to": "Tax Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tax Withholding Category", + "link_to": "Tax Withholding Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Profitability", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Gross Profit", + "link_to": "Gross Profit", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Profitability Analysis", + "link_to": "Profitability Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Invoice Trends", + "link_to": "Sales Invoice Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Invoice Trends", + "link_to": "Purchase Invoice Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:30.922133", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 094e1652b3..30ac23d60f 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -29,7 +29,129 @@ "idx": 0, "is_standard": 1, "label": "Agriculture", - "modified": "2020-06-30 18:35:25.350213", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Crops & Lands", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Crop", + "link_to": "Crop", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Crop Cycle", + "link_to": "Crop Cycle", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Location", + "link_to": "Location", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Analytics", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Plant Analysis", + "link_to": "Plant Analysis", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Soil Analysis", + "link_to": "Soil Analysis", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Water Analysis", + "link_to": "Water Analysis", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Soil Texture", + "link_to": "Soil Texture", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Weather", + "link_to": "Weather", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Agriculture Analysis Criteria", + "link_to": "Agriculture Analysis Criteria", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Diseases & Fertilizers", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Disease", + "link_to": "Disease", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fertilizer", + "link_to": "Fertilizer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:25.889184", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 515fc22f05..f5330c083f 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -34,7 +34,138 @@ "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-06-30 18:36:11.169586", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Assets", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset", + "link_to": "Asset", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Location", + "link_to": "Location", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Category", + "link_to": "Asset Category", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Movement", + "link_to": "Asset Movement", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Maintenance Team", + "link_to": "Asset Maintenance Team", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Maintenance", + "link_to": "Asset Maintenance", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Maintenance Log", + "link_to": "Asset Maintenance Log", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Value Adjustment", + "link_to": "Asset Value Adjustment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Repair", + "link_to": "Asset Repair", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Asset Depreciation Ledger", + "link_to": "Asset Depreciation Ledger", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Asset Depreciations and Balances", + "link_to": "Asset Depreciations and Balances", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Asset Maintenance", + "link_to": "Asset Maintenance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:26.576154", "modified_by": "Administrator", "module": "Assets", "name": "Assets", diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 16df8dfdd8..1c1d8f38d8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -28,7 +28,7 @@ { "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\": \"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]" + "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\": \"Items to Order and Receive\",\n \"name\": \"Requested Items to Order and Receive\",\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, @@ -61,7 +61,416 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-10-21 12:29:02.772723", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Buying", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Material Request", + "link_to": "Material Request", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Order", + "link_to": "Purchase Order", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Request for Quotation", + "link_to": "Request for Quotation", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Quotation", + "link_to": "Supplier Quotation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Items & Pricing", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Price", + "link_to": "Item Price", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Price List", + "link_to": "Price List", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Product Bundle", + "link_to": "Product Bundle", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Group", + "link_to": "Item Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Promotional Scheme", + "link_to": "Promotional Scheme", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Pricing Rule", + "link_to": "Pricing Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Buying Settings", + "link_to": "Buying Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Taxes and Charges Template", + "link_to": "Purchase Taxes and Charges Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Terms and Conditions Template", + "link_to": "Terms and Conditions", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier", + "link_to": "Supplier", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Group", + "link_to": "Supplier Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Contact", + "link_to": "Contact", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Address", + "link_to": "Address", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Scorecard", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Scorecard", + "link_to": "Supplier Scorecard", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Scorecard Variable", + "link_to": "Supplier Scorecard Variable", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Scorecard Criteria", + "link_to": "Supplier Scorecard Criteria", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier Scorecard Standing", + "link_to": "Supplier Scorecard Standing", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Analytics", + "link_to": "Purchase Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Supplier-Wise Sales Analytics", + "link_to": "Supplier-Wise Sales Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Items to Order and Receive", + "link_to": "Requested Items to Order and Receive", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Trends", + "link_to": "Purchase Order Trends", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Procurement Tracker", + "link_to": "Procurement Tracker", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Other Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Items To Be Requested", + "link_to": "Items To Be Requested", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item-wise Purchase History", + "link_to": "Item-wise Purchase History", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Receipt Trends", + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Invoice Trends", + "link_to": "Purchase Invoice Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Subcontracted Raw Materials To Be Transferred", + "link_to": "Subcontracted Raw Materials To Be Transferred", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Subcontracted Item To Be Received", + "link_to": "Subcontracted Item To Be Received", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Supplier Quotation Comparison", + "link_to": "Supplier Quotation Comparison", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Material Requests for which Supplier Quotations are not created", + "link_to": "Material Requests for which Supplier Quotations are not created", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Supplier Addresses And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Regional", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Import Supplier Invoice", + "link_to": "Import Supplier Invoice", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:25.184766", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 5497f3e511..3238962a28 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -43,7 +43,323 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-08-11 18:55:18.238900", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Pipeline", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lead", + "link_to": "Lead", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Opportunity", + "link_to": "Opportunity", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Contact", + "link_to": "Contact", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Communication", + "link_to": "Communication", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lead Source", + "link_to": "Lead Source", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Contract", + "link_to": "Contract", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appointment", + "link_to": "Appointment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Newsletter", + "link_to": "Newsletter", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Lead Details", + "link_to": "Lead Details", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Funnel", + "link_to": "sales-funnel", + "link_type": "Page", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Prospects Engaged But Not Converted", + "link_to": "Prospects Engaged But Not Converted", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "First Response Time for Opportunity", + "link_to": "First Response Time for Opportunity", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Inactive Customers", + "link_to": "Inactive Customers", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Campaign Efficiency", + "link_to": "Campaign Efficiency", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Lead Owner Efficiency", + "link_to": "Lead Owner Efficiency", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance Schedule", + "link_to": "Maintenance Schedule", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance Visit", + "link_to": "Maintenance Visit", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Warranty Claim", + "link_to": "Warranty Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Campaign", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Campaign", + "link_to": "Campaign", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Campaign", + "link_to": "Email Campaign", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Social Media Post", + "link_to": "Social Media Post", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer Group", + "link_to": "Customer Group", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Territory", + "link_to": "Territory", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Person", + "link_to": "Sales Person", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "SMS Center", + "link_to": "SMS Center", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "SMS Log", + "link_to": "SMS Log", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "SMS Settings", + "link_to": "SMS Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Group", + "link_to": "Email Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Twitter Settings", + "link_to": "Twitter Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "LinkedIn Settings", + "link_to": "LinkedIn Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:28.196723", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index 0d51048ab3..dd5338926d 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -84,7 +84,568 @@ "idx": 0, "is_standard": 1, "label": "Education", - "modified": "2020-07-27 19:35:18.832694", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Student and Instructor", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student", + "link_to": "Student", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Instructor", + "link_to": "Instructor", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Guardian", + "link_to": "Guardian", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Group", + "link_to": "Student Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Log", + "link_to": "Student Log", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Masters", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Program", + "link_to": "Program", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course", + "link_to": "Course", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Topic", + "link_to": "Topic", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Room", + "link_to": "Room", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Content Masters", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Article", + "link_to": "Article", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Video", + "link_to": "Video", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quiz", + "link_to": "Quiz", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Education Settings", + "link_to": "Education Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Category", + "link_to": "Student Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Batch Name", + "link_to": "Student Batch Name", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Grading Scale", + "link_to": "Grading Scale", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Academic Term", + "link_to": "Academic Term", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Academic Year", + "link_to": "Academic Year", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Admission", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Applicant", + "link_to": "Student Applicant", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Admission", + "link_to": "Student Admission", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Program Enrollment", + "link_to": "Program Enrollment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Enrollment", + "link_to": "Course Enrollment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fees", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fee Structure", + "link_to": "Fee Structure", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fee Category", + "link_to": "Fee Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fee Schedule", + "link_to": "Fee Schedule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fees", + "link_to": "Fees", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Student Fee Collection Report", + "link_to": "Student Fee Collection", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Program wise Fee Collection Report", + "link_to": "Program wise Fee Collection", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Schedule", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Schedule", + "link_to": "Course Schedule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Scheduling Tool", + "link_to": "Course Scheduling Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Attendance", + "link_to": "Student Attendance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Leave Application", + "link_to": "Student Leave Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Student Monthly Attendance Sheet", + "link_to": "Student Monthly Attendance Sheet", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Absent Student Report", + "link_to": "Absent Student Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Student Batch-Wise Attendance", + "link_to": "Student Batch-Wise Attendance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "LMS Activity", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Enrollment", + "link_to": "Course Enrollment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Activity", + "link_to": "Course Activity", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quiz Activity", + "link_to": "Quiz Activity", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Plan", + "link_to": "Assessment Plan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Group", + "link_to": "Assessment Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Result", + "link_to": "Assessment Result", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Criteria", + "link_to": "Assessment Criteria", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Course wise Assessment Report", + "link_to": "Course wise Assessment Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Final Assessment Grades", + "link_to": "Final Assessment Grades", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Assessment Plan Status", + "link_to": "Assessment Plan Status", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Report Generation Tool", + "link_to": "Student Report Generation Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tools", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Attendance Tool", + "link_to": "Student Attendance Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Assessment Result Tool", + "link_to": "Assessment Result Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student Group Creation Tool", + "link_to": "Student Group Creation Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Program Enrollment Tool", + "link_to": "Program Enrollment Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course Scheduling Tool", + "link_to": "Course Scheduling Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Other Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Student and Guardian Contact Details", + "link_to": "Student and Guardian Contact Details", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:27.243450", "modified_by": "Administrator", "module": "Education", "name": "Education", @@ -95,7 +656,7 @@ "restrict_to_domain": "Education", "shortcuts": [ { - "color": "#cef6d1", + "color": "Grey", "format": "{} Active", "label": "Student", "link_to": "Student", @@ -103,7 +664,7 @@ "type": "DocType" }, { - "color": "#cef6d1", + "color": "Grey", "format": "{} Active", "label": "Instructor", "link_to": "Instructor", @@ -124,7 +685,7 @@ "type": "DocType" }, { - "color": "#ffe8cd", + "color": "Grey", "format": "{} Unpaid", "label": "Fees", "link_to": "Fees", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json index ea3b1291b7..cffa5a2e2c 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json @@ -29,7 +29,93 @@ "idx": 0, "is_standard": 1, "label": "ERPNext Integrations", - "modified": "2020-10-29 19:54:46.228222", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Marketplace", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Woocommerce Settings", + "link_to": "Woocommerce Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Amazon MWS Settings", + "link_to": "Amazon MWS Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shopify Settings", + "link_to": "Shopify Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payments", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "GoCardless Settings", + "link_to": "GoCardless Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "M-Pesa Settings", + "link_to": "Mpesa Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Plaid Settings", + "link_to": "Plaid Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exotel Settings", + "link_to": "Exotel Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:30.437617", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json index 3bbc36ad94..fd5d4dc1c3 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -19,7 +19,61 @@ "idx": 0, "is_standard": 1, "label": "ERPNext Integrations Settings", - "modified": "2020-07-31 10:44:39.374297", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Integrations Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Woocommerce Settings", + "link_to": "Woocommerce Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shopify Settings", + "link_to": "Shopify Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Amazon MWS Settings", + "link_to": "Amazon MWS Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Plaid Settings", + "link_to": "Plaid Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exotel Settings", + "link_to": "Exotel Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:31.919189", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations Settings", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 353d86f4d7..63751e3234 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -65,7 +65,432 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-09-10 15:37:23.666787", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Masters", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient", + "link_to": "Patient", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Healthcare Practitioner", + "link_to": "Healthcare Practitioner", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Practitioner Schedule", + "link_to": "Practitioner Schedule", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Medical Department", + "link_to": "Medical Department", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Healthcare Service Unit Type", + "link_to": "Healthcare Service Unit Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Healthcare Service Unit", + "link_to": "Healthcare Service Unit", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Medical Code Standard", + "link_to": "Medical Code Standard", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Medical Code", + "link_to": "Medical Code", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Consultation Setup", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appointment Type", + "link_to": "Appointment Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Clinical Procedure Template", + "link_to": "Clinical Procedure Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Prescription Dosage", + "link_to": "Prescription Dosage", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Prescription Duration", + "link_to": "Prescription Duration", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Antibiotic", + "link_to": "Antibiotic", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Consultation", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Appointment", + "link_to": "Patient Appointment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Clinical Procedure", + "link_to": "Clinical Procedure", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Encounter", + "link_to": "Patient Encounter", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vital Signs", + "link_to": "Vital Signs", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Complaint", + "link_to": "Complaint", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Diagnosis", + "link_to": "Diagnosis", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fee Validity", + "link_to": "Fee Validity", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Healthcare Settings", + "link_to": "Healthcare Settings", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Laboratory Setup", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lab Test Template", + "link_to": "Lab Test Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lab Test Sample", + "link_to": "Lab Test Sample", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lab Test UOM", + "link_to": "Lab Test UOM", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sensitivity", + "link_to": "Sensitivity", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Laboratory", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lab Test", + "link_to": "Lab Test", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sample Collection", + "link_to": "Sample Collection", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Dosage Form", + "link_to": "Dosage Form", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Rehabilitation and Physiotherapy", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Exercise Type", + "link_to": "Exercise Type", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Therapy Type", + "link_to": "Therapy Type", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Therapy Plan", + "link_to": "Therapy Plan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Therapy Session", + "link_to": "Therapy Session", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Assessment Template", + "link_to": "Patient Assessment Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Assessment", + "link_to": "Patient Assessment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Records and History", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient History", + "link_to": "patient_history", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Progress", + "link_to": "patient-progress", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient Medical Record", + "link_to": "Patient Medical Record", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Inpatient Record", + "link_to": "Inpatient Record", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Patient Appointment Analytics", + "link_to": "Patient Appointment Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Lab Test Report", + "link_to": "Lab Test Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:31.675127", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 217b94a414..4a2ff10a2a 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -63,7 +63,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employee Birthday\",\n \"name\": \"Employee Birthday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employees working on a holiday\",\n \"name\": \"Employees working on a holiday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Department Analytics\",\n \"name\": \"Department Analytics\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employee Birthday\",\n \"name\": \"Employee Birthday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employees working on a holiday\",\n \"name\": \"Employees working on a holiday\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -94,7 +94,807 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-08-11 17:04:38.655417", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee", + "link_to": "Employee", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employment Type", + "link_to": "Employment Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Branch", + "link_to": "Branch", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Department", + "link_to": "Department", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Designation", + "link_to": "Designation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Grade", + "link_to": "Employee Grade", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Group", + "link_to": "Employee Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Health Insurance", + "link_to": "Employee Health Insurance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Lifecycle", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Onboarding", + "link_to": "Employee Onboarding", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Skill Map", + "link_to": "Employee Skill Map", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Promotion", + "link_to": "Employee Promotion", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Transfer", + "link_to": "Employee Transfer", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Separation", + "link_to": "Employee Separation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Onboarding Template", + "link_to": "Employee Onboarding Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Separation Template", + "link_to": "Employee Separation Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Management", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Type", + "link_to": "Shift Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Request", + "link_to": "Shift Request", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shift Assignment", + "link_to": "Shift Assignment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leaves", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Application", + "link_to": "Leave Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Allocation", + "link_to": "Leave Allocation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Policy", + "link_to": "Leave Policy", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Period", + "link_to": "Leave Period", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Type", + "link_to": "Leave Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Holiday List", + "link_to": "Holiday List", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Compensatory Leave Request", + "link_to": "Compensatory Leave Request", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Encashment", + "link_to": "Leave Encashment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Leave Block List", + "link_to": "Leave Block List", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Leave Balance", + "link_to": "Employee Leave Balance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure", + "link_to": "Salary Structure", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure Assignment", + "link_to": "Salary Structure Assignment", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Entry", + "link_to": "Payroll Entry", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Slip", + "link_to": "Salary Slip", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Period", + "link_to": "Payroll Period", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Income Tax Slab", + "link_to": "Income Tax Slab", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Component", + "link_to": "Salary Component", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Additional Salary", + "link_to": "Additional Salary", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Retention Bonus", + "link_to": "Retention Bonus", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Incentive", + "link_to": "Employee Incentive", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Register", + "link_to": "Salary Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Attendance Tool", + "link_to": "Employee Attendance Tool", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance", + "link_to": "Attendance", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Attendance Request", + "link_to": "Attendance Request", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Upload Attendance", + "link_to": "Upload Attendance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Checkin", + "link_to": "Employee Checkin", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Monthly Attendance Sheet", + "link_to": "Monthly Attendance Sheet", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Expense Claims", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Expense Claim", + "link_to": "Expense Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Advance", + "link_to": "Employee Advance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "HR Settings", + "link_to": "HR Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Daily Work Summary Group", + "link_to": "Daily Work Summary Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Team Updates", + "link_to": "team-updates", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fleet Management", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vehicle", + "link_to": "Vehicle", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Vehicle Log", + "link_to": "Vehicle Log", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Vehicle Expenses", + "link_to": "Vehicle Expenses", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Recruitment", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Opening", + "link_to": "Job Opening", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Applicant", + "link_to": "Job Applicant", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Offer", + "link_to": "Job Offer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Staffing Plan", + "link_to": "Staffing Plan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loans", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Application", + "link_to": "Loan Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_to": "Loan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Type", + "link_to": "Loan Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Program", + "link_to": "Training Program", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Event", + "link_to": "Training Event", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Result", + "link_to": "Training Result", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Training Feedback", + "link_to": "Training Feedback", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employee Birthday", + "link_to": "Employee Birthday", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Employees working on a holiday", + "link_to": "Employees working on a holiday", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Performance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appraisal", + "link_to": "Appraisal", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Appraisal Template", + "link_to": "Appraisal Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Point Rule", + "link_to": "Energy Point Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Energy Point Log", + "link_to": "Energy Point Log", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax and Benefits", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Declaration", + "link_to": "Employee Tax Exemption Declaration", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Proof Submission", + "link_to": "Employee Tax Exemption Proof Submission", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Other Income", + "link_to": "Employee Other Income", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Application", + "link_to": "Employee Benefit Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Claim", + "link_to": "Employee Benefit Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Category", + "link_to": "Employee Tax Exemption Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Sub Category", + "link_to": "Employee Tax Exemption Sub Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:24.707870", "modified_by": "Administrator", "module": "HR", "name": "HR", diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 7f59348ef9..0156809619 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -39,7 +39,197 @@ "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-10-17 12:59:50.336085", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Type", + "link_to": "Loan Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Application", + "link_to": "Loan Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_to": "Loan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Processes", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Process Loan Security Shortfall", + "link_to": "Process Loan Security Shortfall", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Process Loan Interest Accrual", + "link_to": "Process Loan Interest Accrual", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Disbursement and Repayment", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Disbursement", + "link_to": "Loan Disbursement", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Repayment", + "link_to": "Loan Repayment", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Write Off", + "link_to": "Loan Write Off", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Interest Accrual", + "link_to": "Loan Interest Accrual", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Type", + "link_to": "Loan Security Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Price", + "link_to": "Loan Security Price", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security", + "link_to": "Loan Security", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Pledge", + "link_to": "Loan Security Pledge", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Unpledge", + "link_to": "Loan Security Unpledge", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Security Shortfall", + "link_to": "Loan Security Shortfall", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Loan Repayment and Closure", + "link_to": "Loan Repayment and Closure", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Loan Security Status", + "link_to": "Loan Security Status", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:29.299238", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 3dd86a38bf..020b147916 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -24,11 +24,6 @@ "hidden": 0, "label": "Settings", "links": "[\n {\n \"description\": \"Global settings for all manufacturing processes.\",\n \"label\": \"Manufacturing Settings\",\n \"name\": \"Manufacturing Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Help", - "links": "[\n {\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"type\": \"help\",\n \"youtube_id\": \"ZotgLyp2YFY\"\n }\n]" } ], "category": "Domains", @@ -48,7 +43,242 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-06-30 18:40:04.454826", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Production", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Work Order", + "link_to": "Work Order", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Production Plan", + "link_to": "Production Plan", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Entry", + "link_to": "Stock Entry", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Job Card", + "link_to": "Job Card", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Downtime Entry", + "link_to": "Downtime Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "link_to": "BOM", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Workstation", + "link_to": "Workstation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Operation", + "link_to": "Operation", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Routing", + "link_to": "Routing", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Production Planning Report", + "link_to": "Production Planning Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Work Order Summary", + "link_to": "Work Order Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Quality Inspection Summary", + "link_to": "Quality Inspection Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Downtime Analysis", + "link_to": "Downtime Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Job Card Summary", + "link_to": "Job Card Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "BOM Search", + "link_to": "BOM Search", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Production Analytics", + "link_to": "Production Analytics", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "BOM Operations Time", + "link_to": "BOM Operations Time", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tools", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "BOM Update Tool", + "link_to": "BOM Update Tool", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "BOM Comparison Tool", + "link_to": "bom-comparison-tool", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Manufacturing Settings", + "link_to": "Manufacturing Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:24.140642", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -116,10 +346,10 @@ "type": "Report" }, { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" - } + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + } ] } \ No newline at end of file diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index 24d655ad6f..078e324454 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -44,7 +44,168 @@ "idx": 0, "is_standard": 1, "label": "Non Profit", - "modified": "2020-06-30 18:35:52.770917", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Management", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Type", + "link_to": "Loan Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan Application", + "link_to": "Loan Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loan", + "link_to": "Loan", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Grant Application", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Grant Application", + "link_to": "Grant Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Membership", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Member", + "link_to": "Member", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Membership", + "link_to": "Membership", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Membership Type", + "link_to": "Membership Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Membership Settings", + "link_to": "Membership Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Volunteer", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Volunteer", + "link_to": "Volunteer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Volunteer Type", + "link_to": "Volunteer Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chapter", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chapter", + "link_to": "Chapter", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Donor", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Donor", + "link_to": "Donor", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Donor Type", + "link_to": "Donor Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:26.036726", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index 8fe8c448b3..9247213c34 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -39,7 +39,253 @@ "idx": 0, "is_standard": 1, "label": "Payroll", - "modified": "2020-08-10 19:38:45.976209", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Component", + "link_to": "Salary Component", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure", + "link_to": "Salary Structure", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure Assignment", + "link_to": "Salary Structure Assignment", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Entry", + "link_to": "Payroll Entry", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Slip", + "link_to": "Salary Slip", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Taxation", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Payroll Period", + "link_to": "Payroll Period", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Income Tax Slab", + "link_to": "Income Tax Slab", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Other Income", + "link_to": "Employee Other Income", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Declaration", + "link_to": "Employee Tax Exemption Declaration", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Proof Submission", + "link_to": "Employee Tax Exemption Proof Submission", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Category", + "link_to": "Employee Tax Exemption Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Tax Exemption Sub Category", + "link_to": "Employee Tax Exemption Sub Category", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Compensations", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Additional Salary", + "link_to": "Additional Salary", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Retention Bonus", + "link_to": "Retention Bonus", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Incentive", + "link_to": "Employee Incentive", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Application", + "link_to": "Employee Benefit Application", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Benefit Claim", + "link_to": "Employee Benefit Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Register", + "link_to": "Salary Register", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Payments Based On Payment Mode", + "link_to": "Salary Payments Based On Payment Mode", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Salary Payments via ECS", + "link_to": "Salary Payments via ECS", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Income Tax Deductions", + "link_to": "Income Tax Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Professional Tax Deductions", + "link_to": "Professional Tax Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Provident Fund Deductions", + "link_to": "Provident Fund Deductions", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Bank Remittance", + "link_to": "Bank Remittance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:27.751514", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 3756c5bb50..4ea2963f50 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -34,7 +34,129 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-06-30 18:38:40.130763", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Projects", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Project", + "link_to": "Project", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Task", + "link_to": "Task", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Project Template", + "link_to": "Project Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Project Type", + "link_to": "Project Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Project Update", + "link_to": "Project Update", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Time Tracking", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Timesheet", + "link_to": "Timesheet", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Activity Type", + "link_to": "Activity Type", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Activity Cost", + "link_to": "Activity Cost", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Daily Timesheet Summary", + "link_to": "Daily Timesheet Summary", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Project wise Stock Tracking", + "link_to": "Project wise Stock Tracking", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Project Billing Summary", + "link_to": "Project Billing Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:26.739225", "modified_by": "Administrator", "module": "Projects", "name": "Projects", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 474f052568..499182e082 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -34,7 +34,118 @@ "idx": 0, "is_standard": 1, "label": "Quality", - "modified": "2020-10-27 16:28:54.138055", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Goal and Procedure", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Goal", + "link_to": "Quality Goal", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Procedure", + "link_to": "Quality Procedure", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tree of Procedures", + "link_to": "Quality Procedure", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Feedback", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Feedback", + "link_to": "Quality Feedback", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Feedback Template", + "link_to": "Quality Feedback Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Meeting", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Meeting", + "link_to": "Quality Meeting", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Review and Action", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Non Conformance", + "link_to": "Non Conformance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Review", + "link_to": "Quality Review", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Action", + "link_to": "Quality Action", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:31.488858", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", @@ -43,23 +154,26 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "Grey", "label": "Quality Goal", "link_to": "Quality Goal", "type": "DocType" }, { + "color": "Grey", "doc_view": "Tree", "label": "Quality Procedure", "link_to": "Quality Procedure", "type": "DocType" }, { + "color": "Grey", "label": "Quality Inspection", "link_to": "Quality Inspection", "type": "DocType" }, { - "color": "#ff8989", + "color": "Grey", "doc_view": "", "format": "{} Open", "label": "Quality Review", @@ -68,7 +182,7 @@ "type": "DocType" }, { - "color": "#ff8989", + "color": "Grey", "doc_view": "", "format": "{} Open", "label": "Quality Action", @@ -77,7 +191,7 @@ "type": "DocType" }, { - "color": "#ff8989", + "color": "Grey", "doc_view": "", "format": "{} Open", "label": "Non Conformance", diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index cdafaeaa9b..c5f461abb6 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -29,7 +29,84 @@ "idx": 0, "is_standard": 1, "label": "Retail", - "modified": "2020-09-09 11:46:28.297435", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings & Configurations", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Point-of-Sale Profile", + "link_to": "POS Profile", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "POS Settings", + "link_to": "POS Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loyalty Program", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loyalty Program", + "link_to": "Loyalty Program", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Loyalty Point Entry", + "link_to": "Loyalty Point Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Opening & Closing", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "POS Opening Entry", + "link_to": "POS Opening Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "POS Closing Entry", + "link_to": "POS Closing Entry", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:29.114099", "modified_by": "Administrator", "module": "Selling", "name": "Retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 82831ab61a..23edc259da 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -45,7 +45,458 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-10-21 12:30:12.164433", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Selling", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quotation", + "link_to": "Quotation", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Order", + "link_to": "Sales Order", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Blanket Order", + "link_to": "Blanket Order", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Partner", + "link_to": "Sales Partner", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Person", + "link_to": "Sales Person", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Items and Pricing", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Price", + "link_to": "Item Price", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Price List", + "link_to": "Price List", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Group", + "link_to": "Item Group", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Product Bundle", + "link_to": "Product Bundle", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Promotional Scheme", + "link_to": "Promotional Scheme", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Pricing Rule", + "link_to": "Pricing Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shipping Rule", + "link_to": "Shipping Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Coupon Code", + "link_to": "Coupon Code", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Selling Settings", + "link_to": "Selling Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Terms and Conditions Template", + "link_to": "Terms and Conditions", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Taxes and Charges Template", + "link_to": "Sales Taxes and Charges Template", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lead Source", + "link_to": "Lead Source", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer Group", + "link_to": "Customer Group", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Contact", + "link_to": "Contact", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Address", + "link_to": "Address", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Territory", + "link_to": "Territory", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Campaign", + "link_to": "Campaign", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Analytics", + "link_to": "Sales Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Sales Funnel", + "link_to": "sales-funnel", + "link_type": "Page", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Trends", + "link_to": "Sales Order Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Quotation Trends", + "link_to": "Quotation Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customer Acquisition and Loyalty", + "link_to": "Customer Acquisition and Loyalty", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Inactive Customers", + "link_to": "Inactive Customers", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Person-wise Transaction Summary", + "link_to": "Sales Person-wise Transaction Summary", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item-wise Sales History", + "link_to": "Item-wise Sales History", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Other Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Lead Details", + "link_to": "Lead Details", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customer Addresses And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Available Stock for Packing Items", + "link_to": "Available Stock for Packing Items", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Pending SO Items For Purchase Request", + "link_to": "Pending SO Items For Purchase Request", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Delivery Note Trends", + "link_to": "Delivery Note Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Invoice Trends", + "link_to": "Sales Invoice Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customer Credit Balance", + "link_to": "Customer Credit Balance", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Customers Without Any Sales Transactions", + "link_to": "Customers Without Any Sales Transactions", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Partners Commission", + "link_to": "Sales Partners Commission", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Territory Target Variance Based On Item Group", + "link_to": "Territory Target Variance Based On Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Person Target Variance Based On Item Group", + "link_to": "Sales Person Target Variance Based On Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Partner Target Variance Based On Item Group", + "link_to": "Sales Partner Target Variance based on Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:30.100950", "modified_by": "Administrator", "module": "Selling", "name": "Selling", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index 5c8cd6977b..efa27e9a55 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -14,7 +14,8 @@ "idx": 0, "is_standard": 1, "label": "ERPNext Settings", - "modified": "2020-07-08 12:53:44.904241", + "links": [], + "modified": "2020-11-17 13:00:26.889826", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/desk_page/home/home.json index 23dec32d72..5f7ab0161e 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -59,7 +59,378 @@ "idx": 0, "is_standard": 1, "label": "Home", - "modified": "2020-06-30 18:36:05.637904", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Healthcare", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Patient", + "link_to": "Patient", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Diagnosis", + "link_to": "Diagnosis", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Agriculture", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Crop", + "link_to": "Crop", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Crop Cycle", + "link_to": "Crop Cycle", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Location", + "link_to": "Location", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Fertilizer", + "link_to": "Fertilizer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Education", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Student", + "link_to": "Student", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Course", + "link_to": "Course", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Instructor", + "link_to": "Instructor", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Room", + "link_to": "Room", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Non Profit", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Member", + "link_to": "Member", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Volunteer", + "link_to": "Volunteer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chapter", + "link_to": "Chapter", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Donor", + "link_to": "Donor", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Warehouse", + "link_to": "Warehouse", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Brand", + "link_to": "Brand", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Unit of Measure (UOM)", + "link_to": "UOM", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Reconciliation", + "link_to": "Stock Reconciliation", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Human Resources", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee", + "link_to": "Employee", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Employee Attendance Tool", + "link_to": "Employee Attendance Tool", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Salary Structure", + "link_to": "Salary Structure", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "CRM", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Lead", + "link_to": "Lead", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer Group", + "link_to": "Customer Group", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Territory", + "link_to": "Territory", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Accounting", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Supplier", + "link_to": "Supplier", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Company", + "link_to": "Company", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Accounts", + "link_to": "Account", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Opening Invoice Creation Tool", + "link_to": "Opening Invoice Creation Tool", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Data Import and Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Import Data", + "link_to": "Data Import", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Chart of Accounts Importer", + "link_to": "Chart of Accounts Importer", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Letter Head", + "link_to": "Letter Head", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Account", + "link_to": "Email Account", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:26.284801", "modified_by": "Administrator", "module": "Setup", "name": "Home", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 9f8f346635..86b3767f47 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -59,7 +59,587 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-10-21 12:28:55.503562", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Items and Pricing", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Group", + "link_to": "Item Group", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Product Bundle", + "link_to": "Product Bundle", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Price List", + "link_to": "Price List", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Price", + "link_to": "Item Price", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Shipping Rule", + "link_to": "Shipping Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Pricing Rule", + "link_to": "Pricing Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Alternative", + "link_to": "Item Alternative", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Manufacturer", + "link_to": "Item Manufacturer", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customs Tariff Number", + "link_to": "Customs Tariff Number", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Transactions", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Material Request", + "link_to": "Material Request", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Entry", + "link_to": "Stock Entry", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Delivery Note", + "link_to": "Delivery Note", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Pick List", + "link_to": "Pick List", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Delivery Trip", + "link_to": "Delivery Trip", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Ledger", + "link_to": "Stock Ledger", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Balance", + "link_to": "Stock Balance", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Projected Qty", + "link_to": "Stock Projected Qty", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Summary", + "link_to": "stock-balance", + "link_type": "Page", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Ageing", + "link_to": "Stock Ageing", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item Price Stock", + "link_to": "Item Price Stock", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Settings", + "link_to": "Stock Settings", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Warehouse", + "link_to": "Warehouse", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Unit of Measure (UOM)", + "link_to": "UOM", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Variant Settings", + "link_to": "Item Variant Settings", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Brand", + "link_to": "Brand", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Attribute", + "link_to": "Item Attribute", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "UOM Conversion Factor", + "link_to": "UOM Conversion Factor", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No and Batch", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No", + "link_to": "Serial No", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Batch", + "link_to": "Batch", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Installation Note", + "link_to": "Installation Note", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No Service Contract Expiry", + "link_to": "Serial No Service Contract Expiry", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No Status", + "link_to": "Serial No Status", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No Warranty Expiry", + "link_to": "Serial No Warranty Expiry", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tools", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Stock Reconciliation", + "link_to": "Stock Reconciliation", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Landed Cost Voucher", + "link_to": "Landed Cost Voucher", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Packing Slip", + "link_to": "Packing Slip", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Inspection", + "link_to": "Quality Inspection", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quality Inspection Template", + "link_to": "Quality Inspection Template", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Quick Stock Balance", + "link_to": "Quick Stock Balance", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item-wise Price List Rate", + "link_to": "Item-wise Price List Rate", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Analytics", + "link_to": "Stock Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock Qty vs Serial No Count", + "link_to": "Stock Qty vs Serial No Count", + "link_type": "Report", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Delivery Note Trends", + "link_to": "Delivery Note Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Receipt Trends", + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item Shortage Report", + "link_to": "Item Shortage Report", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Batch-Wise Balance History", + "link_to": "Batch-Wise Balance History", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Other Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Requested Items To Be Transferred", + "link_to": "Requested Items To Be Transferred", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Batch Item Expiry Status", + "link_to": "Batch Item Expiry Status", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item Prices", + "link_to": "Item Prices", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Itemwise Recommended Reorder Level", + "link_to": "Itemwise Recommended Reorder Level", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Item Variant Details", + "link_to": "Item Variant Details", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Subcontracted Raw Materials To Be Transferred", + "link_to": "Subcontracted Raw Materials To Be Transferred", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Subcontracted Item To Be Received", + "link_to": "Subcontracted Item To Be Received", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Stock and Account Value Comparison", + "link_to": "Stock and Account Value Comparison", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:29.632812", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index f676ce5ecb..4dd322a9ca 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -44,7 +44,141 @@ "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-08-11 15:49:34.307341", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Issues", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Issue", + "link_to": "Issue", + "link_type": "DocType", + "onboard": 1, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Issue Type", + "link_to": "Issue Type", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Issue Priority", + "link_to": "Issue Priority", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance Schedule", + "link_to": "Maintenance Schedule", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Maintenance Visit", + "link_to": "Maintenance Visit", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Service Level Agreement", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Service Level Agreement", + "link_to": "Service Level Agreement", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Warranty", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Warranty Claim", + "link_to": "Warranty Claim", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Serial No", + "link_to": "Serial No", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Support Settings", + "link_to": "Support Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Reports", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "First Response Time for Issues", + "link_to": "First Response Time for Issues", + "link_type": "Report", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:27.977688", "modified_by": "Administrator", "module": "Support", "name": "Support", diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json index 591eab5ed4..987b651506 100644 --- a/erpnext/utilities/desk_page/utilities/utilities.json +++ b/erpnext/utilities/desk_page/utilities/utilities.json @@ -18,8 +18,35 @@ "idx": 0, "is_standard": 1, "label": "Utilities", - "modified": "2020-09-10 12:33:30.089853", - "modified_by": "user@erpnext.com", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Video", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Video", + "link_to": "Video", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Video Settings", + "link_to": "Video Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + } + ], + "modified": "2020-11-17 13:00:29.198857", + "modified_by": "Administrator", "module": "Utilities", "name": "Utilities", "owner": "user@erpnext.com", From 3e6cdea51f15f095314de67f12b34aea907cdf75 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 17 Nov 2020 13:03:47 +0530 Subject: [PATCH 188/283] feat: delete config python files --- erpnext/config/__init__.py | 0 erpnext/config/accounts.py | 626 --------------------------- erpnext/config/agriculture.py | 70 --- erpnext/config/assets.py | 94 ---- erpnext/config/buying.py | 264 ----------- erpnext/config/crm.py | 236 ---------- erpnext/config/desktop.py | 220 ---------- erpnext/config/docs.py | 3 - erpnext/config/getting_started.py | 268 ------------ erpnext/config/healthcare.py | 254 ----------- erpnext/config/help.py | 273 ------------ erpnext/config/hr.py | 470 -------------------- erpnext/config/hub_node.py | 24 - erpnext/config/integrations.py | 51 --- erpnext/config/loan_management.py | 107 ----- erpnext/config/manufacturing.py | 168 ------- erpnext/config/non_profit.py | 101 ----- erpnext/config/quality_management.py | 73 ---- erpnext/config/retail.py | 48 -- erpnext/config/selling.py | 320 -------------- erpnext/config/settings.py | 117 ----- erpnext/config/stock.py | 361 --------------- erpnext/config/support.py | 105 ----- erpnext/config/website.py | 33 -- 24 files changed, 4286 deletions(-) delete mode 100644 erpnext/config/__init__.py delete mode 100644 erpnext/config/accounts.py delete mode 100644 erpnext/config/agriculture.py delete mode 100644 erpnext/config/assets.py delete mode 100644 erpnext/config/buying.py delete mode 100644 erpnext/config/crm.py delete mode 100644 erpnext/config/desktop.py delete mode 100644 erpnext/config/docs.py delete mode 100644 erpnext/config/getting_started.py delete mode 100644 erpnext/config/healthcare.py delete mode 100644 erpnext/config/help.py delete mode 100644 erpnext/config/hr.py delete mode 100644 erpnext/config/hub_node.py delete mode 100644 erpnext/config/integrations.py delete mode 100644 erpnext/config/loan_management.py delete mode 100644 erpnext/config/manufacturing.py delete mode 100644 erpnext/config/non_profit.py delete mode 100644 erpnext/config/quality_management.py delete mode 100644 erpnext/config/retail.py delete mode 100644 erpnext/config/selling.py delete mode 100644 erpnext/config/settings.py delete mode 100644 erpnext/config/stock.py delete mode 100644 erpnext/config/support.py delete mode 100644 erpnext/config/website.py diff --git a/erpnext/config/__init__.py b/erpnext/config/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py deleted file mode 100644 index 839c4ad84a..0000000000 --- a/erpnext/config/accounts.py +++ /dev/null @@ -1,626 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -import frappe - - -def get_data(): - config = [ - { - "label": _("Accounts Receivable"), - "items": [ - { - "type": "doctype", - "name": "Sales Invoice", - "description": _("Bills raised to Customers."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Customer", - "description": _("Customer database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Payment Entry", - "description": _("Bank/Cash transactions against party or for internal transfer") - }, - { - "type": "doctype", - "name": "Payment Request", - "description": _("Payment Request"), - }, - { - "type": "report", - "name": "Accounts Receivable", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Accounts Receivable Summary", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Sales Register", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Item-wise Sales Register", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Ordered Items To Be Billed", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Delivered Items To Be Billed", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - ] - }, - { - "label": _("Accounts Payable"), - "items": [ - { - "type": "doctype", - "name": "Purchase Invoice", - "description": _("Bills raised by Suppliers."), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Supplier", - "description": _("Supplier database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Payment Entry", - "description": _("Bank/Cash transactions against party or for internal transfer") - }, - { - "type": "report", - "name": "Accounts Payable", - "doctype": "Purchase Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Accounts Payable Summary", - "doctype": "Purchase Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Purchase Register", - "doctype": "Purchase Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Item-wise Purchase Register", - "is_query_report": True, - "doctype": "Purchase Invoice" - }, - { - "type": "report", - "name": "Purchase Order Items To Be Billed", - "is_query_report": True, - "doctype": "Purchase Invoice" - }, - { - "type": "report", - "name": "Received Items To Be Billed", - "is_query_report": True, - "doctype": "Purchase Invoice" - }, - ] - }, - { - "label": _("Accounting Masters"), - "items": [ - { - "type": "doctype", - "name": "Company", - "description": _("Company (not Customer or Supplier) master."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Account", - "icon": "fa fa-sitemap", - "label": _("Chart of Accounts"), - "route": "#Tree/Account", - "description": _("Tree of financial accounts."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Accounts Settings", - }, - { - "type": "doctype", - "name": "Fiscal Year", - "description": _("Financial / accounting year.") - }, - { - "type": "doctype", - "name": "Accounting Dimension", - }, - { - "type": "doctype", - "name": "Finance Book", - }, - { - "type": "doctype", - "name": "Accounting Period", - }, - { - "type": "doctype", - "name": "Payment Term", - "description": _("Payment Terms based on conditions") - }, - ] - }, - { - "label": _("Banking and Payments"), - "items": [ - { - "type": "doctype", - "label": _("Match Payments with Invoices"), - "name": "Payment Reconciliation", - "description": _("Match non-linked Invoices and Payments.") - }, - { - "type": "doctype", - "label": _("Update Bank Clearance Dates"), - "name": "Bank Clearance", - "description": _("Update bank payment dates with journals.") - }, - { - "type": "doctype", - "label": _("Invoice Discounting"), - "name": "Invoice Discounting", - }, - { - "type": "report", - "name": "Bank Reconciliation Statement", - "is_query_report": True, - "doctype": "Journal Entry" - },{ - "type": "page", - "name": "bank-reconciliation", - "label": _("Bank Reconciliation"), - "icon": "fa fa-bar-chart" - }, - { - "type": "report", - "name": "Bank Clearance Summary", - "is_query_report": True, - "doctype": "Journal Entry" - }, - { - "type": "doctype", - "name": "Bank Guarantee" - }, - { - "type": "doctype", - "name": "Cheque Print Template", - "description": _("Setup cheque dimensions for printing") - }, - ] - }, - { - "label": _("General Ledger"), - "items": [ - { - "type": "doctype", - "name": "Journal Entry", - "description": _("Accounting journal entries.") - }, - { - "type": "report", - "name": "General Ledger", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Customer Ledger Summary", - "doctype": "Sales Invoice", - "is_query_report": True, - }, - { - "type": "report", - "name": "Supplier Ledger Summary", - "doctype": "Sales Invoice", - "is_query_report": True, - }, - { - "type": "doctype", - "name": "Process Deferred Accounting" - } - ] - }, - { - "label": _("Taxes"), - "items": [ - { - "type": "doctype", - "name": "Sales Taxes and Charges Template", - "description": _("Tax template for selling transactions.") - }, - { - "type": "doctype", - "name": "Purchase Taxes and Charges Template", - "description": _("Tax template for buying transactions.") - }, - { - "type": "doctype", - "name": "Item Tax Template", - "description": _("Tax template for item tax rates.") - }, - { - "type": "doctype", - "name": "Tax Category", - "description": _("Tax Category for overriding tax rates.") - }, - { - "type": "doctype", - "name": "Tax Rule", - "description": _("Tax Rule for transactions.") - }, - { - "type": "doctype", - "name": "Tax Withholding Category", - "description": _("Tax Withholding rates to be applied on transactions.") - }, - ] - }, - { - "label": _("Cost Center and Budgeting"), - "items": [ - { - "type": "doctype", - "name": "Cost Center", - "icon": "fa fa-sitemap", - "label": _("Chart of Cost Centers"), - "route": "#Tree/Cost Center", - "description": _("Tree of financial Cost Centers."), - }, - { - "type": "doctype", - "name": "Budget", - "description": _("Define budget for a financial year.") - }, - { - "type": "doctype", - "name": "Accounting Dimension", - }, - { - "type": "report", - "name": "Budget Variance Report", - "is_query_report": True, - "doctype": "Cost Center" - }, - { - "type": "doctype", - "name": "Monthly Distribution", - "description": _("Seasonality for setting budgets, targets etc.") - }, - ] - }, - { - "label": _("Financial Statements"), - "items": [ - { - "type": "report", - "name": "Trial Balance", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Profit and Loss Statement", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Balance Sheet", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Cash Flow", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Consolidated Financial Statement", - "doctype": "GL Entry", - "is_query_report": True - }, - ] - }, - { - "label": _("Opening and Closing"), - "items": [ - { - "type": "doctype", - "name": "Opening Invoice Creation Tool", - }, - { - "type": "doctype", - "name": "Chart of Accounts Importer", - }, - { - "type": "doctype", - "name": "Period Closing Voucher", - "description": _("Close Balance Sheet and book Profit or Loss.") - }, - ] - - }, - { - "label": _("Multi Currency"), - "items": [ - { - "type": "doctype", - "name": "Currency", - "description": _("Enable / disable currencies.") - }, - { - "type": "doctype", - "name": "Currency Exchange", - "description": _("Currency exchange rate master.") - }, - { - "type": "doctype", - "name": "Exchange Rate Revaluation", - "description": _("Exchange Rate Revaluation master.") - }, - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "Payment Gateway Account", - "description": _("Setup Gateway accounts.") - }, - { - "type": "doctype", - "name": "Terms and Conditions", - "label": _("Terms and Conditions Template"), - "description": _("Template of terms or contract.") - }, - { - "type": "doctype", - "name": "Mode of Payment", - "description": _("e.g. Bank, Cash, Credit Card") - }, - ] - }, - { - "label": _("Subscription Management"), - "items": [ - { - "type": "doctype", - "name": "Subscriber", - }, - { - "type": "doctype", - "name": "Subscription Plan", - }, - { - "type": "doctype", - "name": "Subscription" - }, - { - "type": "doctype", - "name": "Subscription Settings" - } - ] - }, - { - "label": _("Bank Statement"), - "items": [ - { - "type": "doctype", - "label": _("Bank"), - "name": "Bank", - }, - { - "type": "doctype", - "label": _("Bank Account"), - "name": "Bank Account", - }, - { - "type": "doctype", - "name": "Bank Statement Transaction Entry", - }, - { - "type": "doctype", - "label": _("Bank Statement Settings"), - "name": "Bank Statement Settings", - }, - ] - }, - { - "label": _("Profitability"), - "items": [ - { - "type": "report", - "name": "Gross Profit", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Profitability Analysis", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Sales Invoice Trends", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Purchase Invoice Trends", - "is_query_report": True, - "doctype": "Purchase Invoice" - }, - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-table", - "items": [ - { - "type": "report", - "name": "Trial Balance for Party", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Payment Period Based On Invoice Date", - "is_query_report": True, - "doctype": "Journal Entry" - }, - { - "type": "report", - "name": "Sales Partners Commission", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - { - "type": "report", - "is_query_report": True, - "name": "Customer Credit Balance", - "doctype": "Customer" - }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Payment Summary", - "doctype": "Sales Invoice" - }, - { - "type": "report", - "is_query_report": True, - "name": "Address And Contacts", - "doctype": "Address" - } - ] - }, - { - "label": _("Share Management"), - "icon": "fa fa-microchip ", - "items": [ - { - "type": "doctype", - "name": "Shareholder", - "description": _("List of available Shareholders with folio numbers") - }, - { - "type": "doctype", - "name": "Share Transfer", - "description": _("List of all share transactions"), - }, - { - "type": "report", - "name": "Share Ledger", - "doctype": "Share Transfer", - "is_query_report": True - }, - { - "type": "report", - "name": "Share Balance", - "doctype": "Share Transfer", - "is_query_report": True - } - ] - }, - - ] - - gst = { - "label": _("Goods and Services Tax (GST India)"), - "items": [ - { - "type": "doctype", - "name": "GST Settings", - }, - { - "type": "doctype", - "name": "GST HSN Code", - }, - { - "type": "report", - "name": "GSTR-1", - "is_query_report": True - }, - { - "type": "report", - "name": "GSTR-2", - "is_query_report": True - }, - { - "type": "doctype", - "name": "GSTR 3B Report", - }, - { - "type": "report", - "name": "GST Sales Register", - "is_query_report": True - }, - { - "type": "report", - "name": "GST Purchase Register", - "is_query_report": True - }, - { - "type": "report", - "name": "GST Itemised Sales Register", - "is_query_report": True - }, - { - "type": "report", - "name": "GST Itemised Purchase Register", - "is_query_report": True - }, - { - "type": "doctype", - "name": "C-Form", - "description": _("C-Form records"), - "country": "India" - }, - ] - } - - - countries = frappe.get_all("Company", fields="country") - countries = [country["country"] for country in countries] - if "India" in countries: - config.insert(9, gst) - domains = frappe.get_active_domains() - return config diff --git a/erpnext/config/agriculture.py b/erpnext/config/agriculture.py deleted file mode 100644 index 937d76ef7b..0000000000 --- a/erpnext/config/agriculture.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Crops & Lands"), - "items": [ - { - "type": "doctype", - "name": "Crop", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Crop Cycle", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Location", - "onboard": 1, - } - ] - }, - { - "label": _("Diseases & Fertilizers"), - "items": [ - { - "type": "doctype", - "name": "Disease", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Fertilizer", - "onboard": 1, - } - ] - }, - { - "label": _("Analytics"), - "items": [ - { - "type": "doctype", - "name": "Plant Analysis", - }, - { - "type": "doctype", - "name": "Soil Analysis", - }, - { - "type": "doctype", - "name": "Water Analysis", - }, - { - "type": "doctype", - "name": "Soil Texture", - }, - { - "type": "doctype", - "name": "Weather", - }, - { - "type": "doctype", - "name": "Agriculture Analysis Criteria", - } - ] - }, - ] \ No newline at end of file diff --git a/erpnext/config/assets.py b/erpnext/config/assets.py deleted file mode 100644 index 4cf7cf0806..0000000000 --- a/erpnext/config/assets.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Assets"), - "items": [ - { - "type": "doctype", - "name": "Asset", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Location", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Asset Category", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Asset Movement", - "description": _("Transfer an asset from one warehouse to another") - }, - ] - }, - { - "label": _("Maintenance"), - "items": [ - { - "type": "doctype", - "name": "Asset Maintenance Team", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Asset Maintenance", - "onboard": 1, - "dependencies": ["Asset Maintenance Team"], - }, - { - "type": "doctype", - "name": "Asset Maintenance Tasks", - "onboard": 1, - "dependencies": ["Asset Maintenance"], - }, - { - "type": "doctype", - "name": "Asset Maintenance Log", - "dependencies": ["Asset Maintenance"], - }, - { - "type": "doctype", - "name": "Asset Value Adjustment", - "dependencies": ["Asset"], - }, - { - "type": "doctype", - "name": "Asset Repair", - "dependencies": ["Asset"], - }, - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-table", - "items": [ - { - "type": "report", - "name": "Asset Depreciation Ledger", - "doctype": "Asset", - "is_query_report": True, - "dependencies": ["Asset"], - }, - { - "type": "report", - "name": "Asset Depreciations and Balances", - "doctype": "Asset", - "is_query_report": True, - "dependencies": ["Asset"], - }, - { - "type": "report", - "name": "Asset Maintenance", - "doctype": "Asset Maintenance", - "dependencies": ["Asset Maintenance"] - }, - ] - } - ] diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py deleted file mode 100644 index b06bb76ca8..0000000000 --- a/erpnext/config/buying.py +++ /dev/null @@ -1,264 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ - -def get_data(): - config = [ - { - "label": _("Purchasing"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Material Request", - "onboard": 1, - "dependencies": ["Item"], - "description": _("Request for purchase."), - }, - { - "type": "doctype", - "name": "Purchase Order", - "onboard": 1, - "dependencies": ["Item", "Supplier"], - "description": _("Purchase Orders given to Suppliers."), - }, - { - "type": "doctype", - "name": "Purchase Invoice", - "onboard": 1, - "dependencies": ["Item", "Supplier"] - }, - { - "type": "doctype", - "name": "Request for Quotation", - "onboard": 1, - "dependencies": ["Item", "Supplier"], - "description": _("Request for quotation."), - }, - { - "type": "doctype", - "name": "Supplier Quotation", - "dependencies": ["Item", "Supplier"], - "description": _("Quotations received from Suppliers."), - }, - ] - }, - { - "label": _("Items and Pricing"), - "items": [ - { - "type": "doctype", - "name": "Item", - "onboard": 1, - "description": _("All Products or Services."), - }, - { - "type": "doctype", - "name": "Item Price", - "description": _("Multiple Item prices."), - "onboard": 1, - "route": "#Report/Item Price" - }, - { - "type": "doctype", - "name": "Price List", - "description": _("Price List master.") - }, - { - "type": "doctype", - "name": "Pricing Rule", - "description": _("Rules for applying pricing and discount.") - }, - { - "type": "doctype", - "name": "Product Bundle", - "description": _("Bundle items at time of sale."), - }, - { - "type": "doctype", - "name": "Item Group", - "icon": "fa fa-sitemap", - "label": _("Item Group"), - "link": "Tree/Item Group", - "description": _("Tree of Item Groups."), - }, - { - "type": "doctype", - "name": "Promotional Scheme", - "description": _("Rules for applying different promotional schemes.") - } - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "Buying Settings", - "settings": 1, - "description": _("Default settings for buying transactions.") - }, - { - "type": "doctype", - "name": "Purchase Taxes and Charges Template", - "description": _("Tax template for buying transactions.") - }, - { - "type": "doctype", - "name":"Terms and Conditions", - "label": _("Terms and Conditions Template"), - "description": _("Template of terms or contract.") - }, - ] - }, - { - "label": _("Supplier"), - "items": [ - { - "type": "doctype", - "name": "Supplier", - "onboard": 1, - "description": _("Supplier database."), - }, - { - "type": "doctype", - "name": "Supplier Group", - "description": _("Supplier Group master.") - }, - { - "type": "doctype", - "name": "Contact", - "description": _("All Contacts."), - }, - { - "type": "doctype", - "name": "Address", - "description": _("All Addresses."), - }, - - ] - }, - { - "label": _("Key Reports"), - "icon": "fa fa-table", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Purchase Analytics", - "reference_doctype": "Purchase Order", - "onboard": 1 - }, - { - "type": "report", - "is_query_report": True, - "name": "Purchase Order Trends", - "reference_doctype": "Purchase Order", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Procurement Tracker", - "reference_doctype": "Purchase Order", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Requested Items To Order", - "reference_doctype": "Material Request", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Address And Contacts", - "label": _("Supplier Addresses And Contacts"), - "reference_doctype": "Address", - "route_options": { - "party_type": "Supplier" - } - } - ] - }, - { - "label": _("Supplier Scorecard"), - "items": [ - { - "type": "doctype", - "name": "Supplier Scorecard", - "description": _("All Supplier scorecards."), - }, - { - "type": "doctype", - "name": "Supplier Scorecard Variable", - "description": _("Templates of supplier scorecard variables.") - }, - { - "type": "doctype", - "name": "Supplier Scorecard Criteria", - "description": _("Templates of supplier scorecard criteria."), - }, - { - "type": "doctype", - "name": "Supplier Scorecard Standing", - "description": _("Templates of supplier standings."), - }, - - ] - }, - { - "label": _("Other Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Items To Be Requested", - "reference_doctype": "Item", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Item-wise Purchase History", - "reference_doctype": "Item", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Supplier-Wise Sales Analytics", - "reference_doctype": "Stock Ledger Entry", - "onboard": 1 - }, - { - "type": "report", - "is_query_report": True, - "name": "Material Requests for which Supplier Quotations are not created", - "reference_doctype": "Material Request" - } - ] - }, - - ] - - 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/config/crm.py b/erpnext/config/crm.py deleted file mode 100644 index 09c2a65633..0000000000 --- a/erpnext/config/crm.py +++ /dev/null @@ -1,236 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Sales Pipeline"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Lead", - "description": _("Database of potential customers."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Opportunity", - "description": _("Potential opportunities for selling."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Customer", - "description": _("Customer database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Contact", - "description": _("All Contacts."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Communication", - "description": _("Record of all communications of type email, phone, chat, visit, etc."), - }, - { - "type": "doctype", - "name": "Lead Source", - "description": _("Track Leads by Lead Source.") - }, - { - "type": "doctype", - "name": "Contract", - "description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"), - }, - { - "type": "doctype", - "name": "Appointment", - "description" : _("Helps you manage appointments with your leads"), - }, - { - "type": "doctype", - "name": "Newsletter", - "label": _("Newsletter"), - } - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Lead Details", - "doctype": "Lead", - "onboard": 1, - }, - { - "type": "page", - "name": "sales-funnel", - "label": _("Sales Funnel"), - "icon": "fa fa-bar-chart", - "onboard": 1, - }, - { - "type": "report", - "name": "Prospects Engaged But Not Converted", - "doctype": "Lead", - "is_query_report": True, - "onboard": 1, - }, - { - "type": "report", - "name": "Minutes to First Response for Opportunity", - "doctype": "Opportunity", - "is_query_report": True, - "dependencies": ["Opportunity"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Customer Addresses And Contacts", - "doctype": "Contact", - "dependencies": ["Customer"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Inactive Customers", - "doctype": "Sales Order", - "dependencies": ["Sales Order"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Campaign Efficiency", - "doctype": "Lead", - "dependencies": ["Lead"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Lead Owner Efficiency", - "doctype": "Lead", - "dependencies": ["Lead"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Territory-wise Sales", - "doctype": "Opportunity", - "dependencies": ["Opportunity"] - } - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "label": _("Customer Group"), - "name": "Customer Group", - "icon": "fa fa-sitemap", - "link": "Tree/Customer Group", - "description": _("Manage Customer Group Tree."), - "onboard": 1, - }, - { - "type": "doctype", - "label": _("Territory"), - "name": "Territory", - "icon": "fa fa-sitemap", - "link": "Tree/Territory", - "description": _("Manage Territory Tree."), - "onboard": 1, - }, - { - "type": "doctype", - "label": _("Sales Person"), - "name": "Sales Person", - "icon": "fa fa-sitemap", - "link": "Tree/Sales Person", - "description": _("Manage Sales Person Tree."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Campaign", - "description": _("Sales campaigns."), - }, - { - "type": "doctype", - "name": "Email Campaign", - "description": _("Sends Mails to lead or contact based on a Campaign schedule"), - }, - { - "type": "doctype", - "name": "SMS Center", - "description":_("Send mass SMS to your contacts"), - }, - { - "type": "doctype", - "name": "SMS Log", - "description":_("Logs for maintaining sms delivery status"), - }, - { - "type": "doctype", - "name": "SMS Settings", - "description": _("Setup SMS gateway settings") - }, - { - "type": "doctype", - "label": _("Email Group"), - "name": "Email Group", - } - ] - }, - { - "label": _("Maintenance"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Maintenance Schedule", - "description": _("Plan for maintenance visits."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Maintenance Visit", - "description": _("Visit report for maintenance call."), - }, - { - "type": "report", - "name": "Maintenance Schedules", - "is_query_report": True, - "doctype": "Maintenance Schedule" - }, - { - "type": "doctype", - "name": "Warranty Claim", - "description": _("Warranty Claim against Serial No."), - }, - ] - }, - # { - # "label": _("Help"), - # "items": [ - # { - # "type": "help", - # "label": _("Lead to Quotation"), - # "youtube_id": "TxYX4r4JAKA" - # }, - # { - # "type": "help", - # "label": _("Newsletters"), - # "youtube_id": "muLKsCrrDRo" - # }, - # ] - # }, - ] diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py deleted file mode 100644 index ce7c245a63..0000000000 --- a/erpnext/config/desktop.py +++ /dev/null @@ -1,220 +0,0 @@ -# coding=utf-8 - -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - # Modules - { - "module_name": "Getting Started", - "category": "Modules", - "label": _("Getting Started"), - "color": "#1abc9c", - "icon": "fa fa-check-square-o", - "type": "module", - "disable_after_onboard": 1, - "description": "Dive into the basics for your organisation's needs.", - "onboard_present": 1 - }, - { - "module_name": "Accounts", - "category": "Modules", - "label": _("Accounting"), - "color": "#3498db", - "icon": "octicon octicon-repo", - "type": "module", - "description": "Accounts, billing, payments, cost center and budgeting." - }, - { - "module_name": "Selling", - "category": "Modules", - "label": _("Selling"), - "color": "#1abc9c", - "icon": "octicon octicon-tag", - "type": "module", - "description": "Sales orders, quotations, customers and items." - }, - { - "module_name": "Buying", - "category": "Modules", - "label": _("Buying"), - "color": "#c0392b", - "icon": "octicon octicon-briefcase", - "type": "module", - "description": "Purchasing, suppliers, material requests, and items." - }, - { - "module_name": "Stock", - "category": "Modules", - "label": _("Stock"), - "color": "#f39c12", - "icon": "octicon octicon-package", - "type": "module", - "description": "Stock transactions, reports, serial numbers and batches." - }, - { - "module_name": "Assets", - "category": "Modules", - "label": _("Assets"), - "color": "#4286f4", - "icon": "octicon octicon-database", - "type": "module", - "description": "Asset movement, maintainance and tools." - }, - { - "module_name": "Projects", - "category": "Modules", - "label": _("Projects"), - "color": "#8e44ad", - "icon": "octicon octicon-rocket", - "type": "module", - "description": "Updates, Timesheets and Activities." - }, - { - "module_name": "CRM", - "category": "Modules", - "label": _("CRM"), - "color": "#EF4DB6", - "icon": "octicon octicon-broadcast", - "type": "module", - "description": "Sales pipeline, leads, opportunities and customers." - }, - { - "module_name": "Loan Management", - "category": "Modules", - "label": _("Loan Management"), - "color": "#EF4DB6", - "icon": "octicon octicon-repo", - "type": "module", - "description": "Loan Management for Customer and Employees" - }, - { - "module_name": "Support", - "category": "Modules", - "label": _("Support"), - "color": "#1abc9c", - "icon": "fa fa-check-square-o", - "type": "module", - "description": "User interactions, support issues and knowledge base." - }, - { - "module_name": "HR", - "category": "Modules", - "label": _("Human Resources"), - "color": "#2ecc71", - "icon": "octicon octicon-organization", - "type": "module", - "description": "Employees, attendance, payroll, leaves and shifts." - }, - { - "module_name": "Quality Management", - "category": "Modules", - "label": _("Quality"), - "color": "#1abc9c", - "icon": "fa fa-check-square-o", - "type": "module", - "description": "Quality goals, procedures, reviews and action." - }, - - - # Category: "Domains" - { - "module_name": "Manufacturing", - "category": "Domains", - "label": _("Manufacturing"), - "color": "#7f8c8d", - "icon": "octicon octicon-tools", - "type": "module", - "description": "BOMS, work orders, operations, and timesheets." - }, - { - "module_name": "Retail", - "category": "Domains", - "label": _("Retail"), - "color": "#7f8c8d", - "icon": "octicon octicon-credit-card", - "type": "module", - "description": "Point of Sale and cashier closing." - }, - { - "module_name": "Education", - "category": "Domains", - "label": _("Education"), - "color": "#428B46", - "icon": "octicon octicon-mortar-board", - "type": "module", - "description": "Student admissions, fees, courses and scores." - }, - - { - "module_name": "Healthcare", - "category": "Domains", - "label": _("Healthcare"), - "color": "#FF888B", - "icon": "fa fa-heartbeat", - "type": "module", - "description": "Patient appointments, procedures and tests." - }, - { - "module_name": "Agriculture", - "category": "Domains", - "label": _("Agriculture"), - "color": "#8BC34A", - "icon": "octicon octicon-globe", - "type": "module", - "description": "Crop cycles, land areas, soil and plant analysis." - }, - { - "module_name": "Hotels", - "category": "Domains", - "label": _("Hotels"), - "color": "#EA81E8", - "icon": "fa fa-bed", - "type": "module", - "description": "Hotel rooms, pricing, reservation and amenities." - }, - - { - "module_name": "Non Profit", - "category": "Domains", - "label": _("Non Profit"), - "color": "#DE2B37", - "icon": "octicon octicon-heart", - "type": "module", - "description": "Volunteers, memberships, grants and chapters." - }, - { - "module_name": "Restaurant", - "category": "Domains", - "label": _("Restaurant"), - "color": "#EA81E8", - "icon": "fa fa-cutlery", - "_doctype": "Restaurant", - "type": "module", - "link": "List/Restaurant", - "description": "Menu, Orders and Table Reservations." - }, - - { - "module_name": "Help", - "category": "Administration", - "label": _("Learn"), - "color": "#FF888B", - "icon": "octicon octicon-device-camera-video", - "type": "module", - "is_help": True, - "description": "Explore Help Articles and Videos." - }, - { - "module_name": 'Marketplace', - "category": "Places", - "label": _('Marketplace'), - "icon": "octicon octicon-star", - "type": 'link', - "link": '#marketplace/home', - "color": '#FF4136', - 'standard': 1, - "description": "Publish items to other ERPNext users." - }, - ] diff --git a/erpnext/config/docs.py b/erpnext/config/docs.py deleted file mode 100644 index 85e600687f..0000000000 --- a/erpnext/config/docs.py +++ /dev/null @@ -1,3 +0,0 @@ -from __future__ import unicode_literals - -source_link = "https://github.com/erpnext/foundation" diff --git a/erpnext/config/getting_started.py b/erpnext/config/getting_started.py deleted file mode 100644 index dc72316d08..0000000000 --- a/erpnext/config/getting_started.py +++ /dev/null @@ -1,268 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ - -active_domains = frappe.get_active_domains() - -def get_data(): - return [ - { - "label": _("Accounting"), - "items": [ - { - "type": "doctype", - "name": "Item", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Customer", - "description": _("Customer database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Supplier", - "description": _("Supplier database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Company", - "description": _("Company (not Customer or Supplier) master."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Account", - "icon": "fa fa-sitemap", - "label": _("Chart of Accounts"), - "route": "#Tree/Account", - "description": _("Tree of financial accounts."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Opening Invoice Creation Tool", - "description": _("Create Opening Sales and Purchase Invoices"), - "onboard": 1, - }, - ] - }, - { - "label": _("Data Import and Settings"), - "items": [ - { - "type": "doctype", - "name": "Data Import", - "label": _("Import Data"), - "icon": "octicon octicon-cloud-upload", - "description": _("Import Data from CSV / Excel files."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Chart of Accounts Importer", - "label": _("Chart of Accounts Importer"), - "description": _("Import Chart of Accounts from CSV / Excel files"), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Letter Head", - "description": _("Letter Heads for print templates."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Email Account", - "description": _("Add / Manage Email Accounts."), - "onboard": 1, - }, - - ] - }, - { - "label": _("Stock"), - "items": [ - { - "type": "doctype", - "name": "Warehouse", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Brand", - "onboard": 1, - }, - { - "type": "doctype", - "name": "UOM", - "label": _("Unit of Measure") + " (UOM)", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Stock Reconciliation", - "onboard": 1, - }, - ] - }, - { - "label": _("CRM"), - "items": [ - { - "type": "doctype", - "name": "Lead", - "description": _("Database of potential customers."), - "onboard": 1, - }, - { - "type": "doctype", - "label": _("Customer Group"), - "name": "Customer Group", - "icon": "fa fa-sitemap", - "link": "Tree/Customer Group", - "description": _("Manage Customer Group Tree."), - "onboard": 1, - }, - { - "type": "doctype", - "label": _("Territory"), - "name": "Territory", - "icon": "fa fa-sitemap", - "link": "Tree/Territory", - "description": _("Manage Territory Tree."), - "onboard": 1, - }, - ] - }, - { - "label": _("Human Resources"), - "items": [ - { - "type": "doctype", - "name": "Employee", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Employee Attendance Tool", - "hide_count": True, - "onboard": 1, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Salary Structure", - "onboard": 1, - }, - ] - }, - { - "label": _("Education"), - "condition": "Education" in active_domains, - "items": [ - { - "type": "doctype", - "name": "Student", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Course", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Instructor", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Room", - "onboard": 1, - }, - ] - }, - { - "label": _("Healthcare"), - "condition": "Healthcare" in active_domains, - "items": [ - { - "type": "doctype", - "name": "Patient", - "label": _("Patient"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Physician", - "label": _("Physician"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Diagnosis", - "label": _("Diagnosis"), - "onboard": 1, - } - ] - }, - { - "label": _("Agriculture"), - "condition": "Agriculture" in active_domains, - "items": [ - { - "type": "doctype", - "name": "Crop", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Crop Cycle", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Location", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Fertilizer", - "onboard": 1, - } - ] - }, - { - "label": _("Non Profit"), - "condition": "Non Profit" in active_domains, - "items": [ - { - "type": "doctype", - "name": "Member", - "description": _("Member information."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Volunteer", - "description": _("Volunteer information."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Chapter", - "description": _("Chapter information."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Donor", - "description": _("Donor information."), - "onboard": 1, - }, - ] - } - ] \ No newline at end of file diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py deleted file mode 100644 index da24d11538..0000000000 --- a/erpnext/config/healthcare.py +++ /dev/null @@ -1,254 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Masters"), - "items": [ - { - "type": "doctype", - "name": "Patient", - "label": _("Patient"), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Healthcare Practitioner", - "label": _("Healthcare Practitioner"), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Practitioner Schedule", - "label": _("Practitioner Schedule"), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Medical Department", - "label": _("Medical Department"), - }, - { - "type": "doctype", - "name": "Healthcare Service Unit Type", - "label": _("Healthcare Service Unit Type") - }, - { - "type": "doctype", - "name": "Healthcare Service Unit", - "label": _("Healthcare Service Unit") - }, - { - "type": "doctype", - "name": "Medical Code Standard", - "label": _("Medical Code Standard") - }, - { - "type": "doctype", - "name": "Medical Code", - "label": _("Medical Code") - } - ] - }, - { - "label": _("Consultation Setup"), - "items": [ - { - "type": "doctype", - "name": "Appointment Type", - "label": _("Appointment Type"), - }, - { - "type": "doctype", - "name": "Clinical Procedure Template", - "label": _("Clinical Procedure Template") - }, - { - "type": "doctype", - "name": "Prescription Dosage", - "label": _("Prescription Dosage") - }, - { - "type": "doctype", - "name": "Prescription Duration", - "label": _("Prescription Duration") - }, - { - "type": "doctype", - "name": "Antibiotic", - "label": _("Antibiotic") - } - ] - }, - { - "label": _("Consultation"), - "items": [ - { - "type": "doctype", - "name": "Patient Appointment", - "label": _("Patient Appointment") - }, - { - "type": "doctype", - "name": "Clinical Procedure", - "label": _("Clinical Procedure") - }, - { - "type": "doctype", - "name": "Patient Encounter", - "label": _("Patient Encounter") - }, - { - "type": "doctype", - "name": "Vital Signs", - "label": _("Vital Signs") - }, - { - "type": "doctype", - "name": "Complaint", - "label": _("Complaint") - }, - { - "type": "doctype", - "name": "Diagnosis", - "label": _("Diagnosis") - }, - { - "type": "doctype", - "name": "Fee Validity", - "label": _("Fee Validity") - } - ] - }, - { - "label": _("Settings"), - "items": [ - { - "type": "doctype", - "name": "Healthcare Settings", - "label": _("Healthcare Settings"), - "onboard": 1 - } - ] - }, - { - "label": _("Laboratory Setup"), - "items": [ - { - "type": "doctype", - "name": "Lab Test Template", - "label": _("Lab Test Template") - }, - { - "type": "doctype", - "name": "Lab Test Sample", - "label": _("Lab Test Sample") - }, - { - "type": "doctype", - "name": "Lab Test UOM", - "label": _("Lab Test UOM") - }, - { - "type": "doctype", - "name": "Sensitivity", - "label": _("Sensitivity") - } - ] - }, - { - "label": _("Laboratory"), - "items": [ - { - "type": "doctype", - "name": "Lab Test", - "label": _("Lab Test") - }, - { - "type": "doctype", - "name": "Sample Collection", - "label": _("Sample Collection") - }, - { - "type": "doctype", - "name": "Dosage Form", - "label": _("Dosage Form") - } - ] - }, - { - "label": _("Records and History"), - "items": [ - { - "type": "page", - "name": "patient_history", - "label": _("Patient History"), - }, - { - "type": "doctype", - "name": "Patient Medical Record", - "label": _("Patient Medical Record") - }, - { - "type": "doctype", - "name": "Inpatient Record", - "label": _("Inpatient Record") - } - ] - }, - { - "label": _("Reports"), - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Patient Appointment Analytics", - "doctype": "Patient Appointment" - }, - { - "type": "report", - "is_query_report": True, - "name": "Lab Test Report", - "doctype": "Lab Test", - "label": _("Lab Test Report") - } - ] - }, - { - "label": _("Rehabilitation"), - "icon": "icon-cog", - "items": [ - { - "type": "doctype", - "name": "Exercise Type", - "label": _("Exercise Type") - }, - { - "type": "doctype", - "name": "Exercise Difficulty Level", - "label": _("Exercise Difficulty Level") - }, - { - "type": "doctype", - "name": "Therapy Type", - "label": _("Therapy Type") - }, - { - "type": "doctype", - "name": "Therapy Plan", - "label": _("Therapy Plan") - }, - { - "type": "doctype", - "name": "Therapy Session", - "label": _("Therapy Session") - }, - { - "type": "doctype", - "name": "Motor Assessment Scale", - "label": _("Motor Assessment Scale") - } - ] - } - ] diff --git a/erpnext/config/help.py b/erpnext/config/help.py deleted file mode 100644 index 922afb4c49..0000000000 --- a/erpnext/config/help.py +++ /dev/null @@ -1,273 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("General"), - "items": [ - { - "type": "help", - "label": _("Navigating"), - "youtube_id": "YDoI2DF4Lmc" - }, - { - "type": "help", - "label": _("Setup Wizard"), - "youtube_id": "oIOf_zCFWKQ" - }, - { - "type": "help", - "label": _("Customizing Forms"), - "youtube_id": "pJhL9mmxV_U" - }, - { - "type": "help", - "label": _("Report Builder"), - "youtube_id": "TxJGUNarcQs" - }, - ] - - }, - { - "label": _("Settings"), - "items": [ - { - "type": "help", - "label": _("Data Import and Export"), - "youtube_id": "6wiriRKPhmg" - }, - { - "type": "help", - "label": _("Opening Stock Balance"), - "youtube_id": "nlHX0ZZ84Lw" - }, - { - "type": "help", - "label": _("Setting up Email Account"), - "youtube_id": "YFYe0DrB95o" - }, - { - "type": "help", - "label": _("Printing and Branding"), - "youtube_id": "cKZHcx1znMc" - }, - { - "type": "help", - "label": _("Users and Permissions"), - "youtube_id": "8Slw1hsTmUI" - }, - { - "type": "help", - "label": _("Workflow"), - "youtube_id": "yObJUg9FxFs" - }, - { - "type": "help", - "label": _("File Manager"), - "youtube_id": "4-osLW3E_Rk" - }, - ] - }, - { - "label": _("Accounting"), - "items": [ - { - "type": "help", - "label": _("Chart of Accounts"), - "youtube_id": "DyR-DST-PyA" - }, - { - "type": "help", - "label": _("Setting up Taxes"), - "youtube_id": "nQ1zZdPgdaQ" - }, - { - "type": "help", - "label": _("Opening Accounting Balance"), - "youtube_id": "kdgM20Q-q68" - }, - { - "type": "help", - "label": _("Advance Payments"), - "youtube_id": "J46-6qtyZ9U" - }, - ] - }, - { - "label": _("CRM"), - "items": [ - { - "type": "help", - "label": _("Lead to Quotation"), - "youtube_id": "TxYX4r4JAKA" - }, - { - "type": "help", - "label": _("Newsletters"), - "youtube_id": "muLKsCrrDRo" - }, - ] - }, - { - "label": _("Selling"), - "items": [ - { - "type": "help", - "label": _("Customer and Supplier"), - "youtube_id": "anoGi_RpQ20" - }, - { - "type": "help", - "label": _("Sales Order to Payment"), - "youtube_id": "1eP90MWoDQM" - }, - { - "type": "help", - "label": _("Point-of-Sale"), - "youtube_id": "4WkelWkbP_c" - }, - { - "type": "help", - "label": _("Product Bundle"), - "youtube_id": "yk3kPrRyRRc" - }, - { - "type": "help", - "label": _("Drop Ship"), - "youtube_id": "hUc0hu_XLdo" - }, - ] - }, - { - "label": _("Stock"), - "items": [ - { - "type": "help", - "label": _("Items and Pricing"), - "youtube_id": "qXaEwld4_Ps" - }, - { - "type": "help", - "label": _("Item Variants"), - "youtube_id": "OGBETlCzU5o" - }, - { - "type": "help", - "label": _("Opening Stock Balance"), - "youtube_id": "0yPgrtfeCTs" - }, - { - "type": "help", - "label": _("Making Stock Entries"), - "youtube_id": "Njt107hlY3I" - }, - { - "type": "help", - "label": _("Serialized Inventory"), - "youtube_id": "gvOVlEwFDAk" - }, - { - "type": "help", - "label": _("Batch Inventory"), - "youtube_id": "J0QKl7ABPKM" - }, - { - "type": "help", - "label": _("Managing Subcontracting"), - "youtube_id": "ThiMCC2DtKo" - }, - { - "type": "help", - "label": _("Quality Inspection"), - "youtube_id": "WmtcF3Y40Fs" - }, - ] - }, - { - "label": _("Buying"), - "items": [ - { - "type": "help", - "label": _("Customer and Supplier"), - "youtube_id": "anoGi_RpQ20" - }, - { - "type": "help", - "label": _("Material Request to Purchase Order"), - "youtube_id": "55Gk2j7Q8Zw" - }, - { - "type": "help", - "label": _("Purchase Order to Payment"), - "youtube_id": "efFajTTQBa8" - }, - { - "type": "help", - "label": _("Managing Subcontracting"), - "youtube_id": "ThiMCC2DtKo" - }, - ] - }, - { - "label": _("Manufacturing"), - "items": [ - { - "type": "help", - "label": _("Bill of Materials"), - "youtube_id": "hDV0c1OeWLo" - }, - { - "type": "help", - "label": _("Work Order"), - "youtube_id": "ZotgLyp2YFY" - }, - - ] - }, - { - "label": _("Human Resource"), - "items": [ - { - "type": "help", - "label": _("Setting up Employees"), - "youtube_id": "USfIUdZlUhw" - }, - { - "type": "help", - "label": _("Leave Management"), - "youtube_id": "fc0p_AXebc8" - }, - { - "type": "help", - "label": _("Expense Claims"), - "youtube_id": "5SZHJF--ZFY" - } - ] - }, - { - "label": _("Projects"), - "items": [ - { - "type": "help", - "label": _("Managing Projects"), - "youtube_id": "gCzShu9Niu4" - }, - ] - }, - { - "label": _("Website"), - "items": [ - { - "type": "help", - "label": _("Publish Items on Website"), - "youtube_id": "W31LBBNzbgc" - }, - { - "type": "help", - "label": _("Shopping Cart"), - "youtube_id": "xkrYO-KFukM" - }, - ] - }, - ] diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py deleted file mode 100644 index 9855a115a6..0000000000 --- a/erpnext/config/hr.py +++ /dev/null @@ -1,470 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Employee"), - "items": [ - { - "type": "doctype", - "name": "Employee", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Employment Type", - }, - { - "type": "doctype", - "name": "Branch", - }, - { - "type": "doctype", - "name": "Department", - }, - { - "type": "doctype", - "name": "Designation", - }, - { - "type": "doctype", - "name": "Employee Grade", - }, - { - "type": "doctype", - "name": "Employee Group", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Health Insurance" - }, - ] - }, - { - "label": _("Attendance"), - "items": [ - { - "type": "doctype", - "name": "Employee Attendance Tool", - "hide_count": True, - "onboard": 1, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Attendance", - "onboard": 1, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Attendance Request", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Upload Attendance", - "hide_count": True, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Checkin", - "hide_count": True, - "dependencies": ["Employee"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Monthly Attendance Sheet", - "doctype": "Attendance" - }, - ] - }, - { - "label": _("Leaves"), - "items": [ - { - "type": "doctype", - "name": "Leave Application", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Leave Allocation", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Leave Policy", - "dependencies": ["Leave Type"] - }, - { - "type": "doctype", - "name": "Leave Period", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name":"Leave Type", - }, - { - "type": "doctype", - "name": "Holiday List", - }, - { - "type": "doctype", - "name": "Compensatory Leave Request", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Leave Encashment", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Leave Block List", - }, - { - "type": "report", - "is_query_report": True, - "name": "Employee Leave Balance", - "doctype": "Leave Application" - }, - { - "type": "report", - "is_query_report": True, - "name": "Leave Ledger Entry", - "doctype": "Leave Ledger Entry" - }, - ] - }, - { - "label": _("Payroll"), - "items": [ - { - "type": "doctype", - "name": "Salary Structure", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Salary Structure Assignment", - "onboard": 1, - "dependencies": ["Salary Structure", "Employee"], - }, - { - "type": "doctype", - "name": "Payroll Entry", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Salary Slip", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Payroll Period", - }, - { - "type": "doctype", - "name": "Income Tax Slab", - }, - { - "type": "doctype", - "name": "Salary Component", - }, - { - "type": "doctype", - "name": "Additional Salary", - }, - { - "type": "doctype", - "name": "Retention Bonus", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Incentive", - "dependencies": ["Employee"] - }, - { - "type": "report", - "is_query_report": True, - "name": "Salary Register", - "doctype": "Salary Slip" - }, - ] - }, - { - "label": _("Employee Tax and Benefits"), - "items": [ - { - "type": "doctype", - "name": "Employee Tax Exemption Declaration", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Tax Exemption Proof Submission", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Other Income", - }, - { - "type": "doctype", - "name": "Employee Benefit Application", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Benefit Claim", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Tax Exemption Category", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Tax Exemption Sub Category", - "dependencies": ["Employee"] - }, - ] - }, - { - "label": _("Employee Lifecycle"), - "items": [ - { - "type": "doctype", - "name": "Employee Onboarding", - "dependencies": ["Job Applicant"], - }, - { - "type": "doctype", - "name": "Employee Skill Map", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Promotion", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Transfer", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Separation", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Onboarding Template", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Separation Template", - "dependencies": ["Employee"] - }, - ] - }, - { - "label": _("Recruitment"), - "items": [ - { - "type": "doctype", - "name": "Job Opening", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Job Applicant", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Job Offer", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Appointment Letter", - }, - { - "type": "doctype", - "name": "Staffing Plan", - }, - ] - }, - { - "label": _("Training"), - "items": [ - { - "type": "doctype", - "name": "Training Program" - }, - { - "type": "doctype", - "name": "Training Event" - }, - { - "type": "doctype", - "name": "Training Result" - }, - { - "type": "doctype", - "name": "Training Feedback" - }, - ] - }, - { - "label": _("Performance"), - "items": [ - { - "type": "doctype", - "name": "Appraisal", - }, - { - "type": "doctype", - "name": "Appraisal Template", - }, - { - "type": "doctype", - "name": "Energy Point Rule", - }, - { - "type": "doctype", - "name": "Energy Point Log", - }, - { - "type": "link", - "doctype": "Energy Point Log", - "label": _("Energy Point Leaderboard"), - "route": "#social/users" - }, - ] - }, - { - "label": _("Expense Claims"), - "items": [ - { - "type": "doctype", - "name": "Expense Claim", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Advance", - "dependencies": ["Employee"] - }, - ] - }, - { - "label": _("Loans"), - "items": [ - { - "type": "doctype", - "name": "Loan Application", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Loan" - }, - { - "type": "doctype", - "name": "Loan Type", - }, - ] - }, - { - "label": _("Shift Management"), - "items": [ - { - "type": "doctype", - "name": "Shift Type", - }, - { - "type": "doctype", - "name": "Shift Request", - }, - { - "type": "doctype", - "name": "Shift Assignment", - }, - ] - }, - { - "label": _("Fleet Management"), - "items": [ - { - "type": "doctype", - "name": "Vehicle" - }, - { - "type": "doctype", - "name": "Vehicle Log" - }, - { - "type": "report", - "is_query_report": True, - "name": "Vehicle Expenses", - "doctype": "Vehicle" - }, - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "HR Settings", - }, - { - "type": "doctype", - "name": "Daily Work Summary Group" - }, - { - "type": "page", - "name": "team-updates", - "label": _("Team Updates") - }, - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Employee Birthday", - "doctype": "Employee" - }, - { - "type": "report", - "is_query_report": True, - "name": "Employees working on a holiday", - "doctype": "Employee" - }, - { - "type": "report", - "is_query_report": True, - "name": "Department Analytics", - "doctype": "Employee" - }, - ] - }, - ] diff --git a/erpnext/config/hub_node.py b/erpnext/config/hub_node.py deleted file mode 100644 index 0afdeb52b1..0000000000 --- a/erpnext/config/hub_node.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Settings"), - "items": [ - { - "type": "doctype", - "name": "Marketplace Settings" - }, - ] - }, - { - "label": _("Marketplace"), - "items": [ - { - "type": "page", - "name": "marketplace/home" - }, - ] - }, - ] \ No newline at end of file diff --git a/erpnext/config/integrations.py b/erpnext/config/integrations.py deleted file mode 100644 index f8b3257b5c..0000000000 --- a/erpnext/config/integrations.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Payments"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "GoCardless Settings", - "description": _("GoCardless payment gateway settings"), - }, - { - "type": "doctype", - "name": "GoCardless Mandate", - "description": _("GoCardless SEPA Mandate"), - } - ] - }, - { - "label": _("Settings"), - "items": [ - { - "type": "doctype", - "name": "Woocommerce Settings" - }, - { - "type": "doctype", - "name": "Shopify Settings", - "description": _("Connect Shopify with ERPNext"), - }, - { - "type": "doctype", - "name": "Amazon MWS Settings", - "description": _("Connect Amazon with ERPNext"), - }, - { - "type": "doctype", - "name": "Plaid Settings", - "description": _("Connect your bank accounts to ERPNext"), - }, - { - "type": "doctype", - "name": "Exotel Settings", - "description": _("Connect your Exotel Account to ERPNext and track call logs"), - } - ] - } - ] diff --git a/erpnext/config/loan_management.py b/erpnext/config/loan_management.py deleted file mode 100644 index a84f13abab..0000000000 --- a/erpnext/config/loan_management.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -import frappe - - -def get_data(): - return [ - { - "label": _("Loan"), - "items": [ - { - "type": "doctype", - "name": "Loan Type", - "description": _("Loan Type for interest and penalty rates"), - }, - { - "type": "doctype", - "name": "Loan Application", - "description": _("Loan Applications from customers and employees."), - }, - { - "type": "doctype", - "name": "Loan", - "description": _("Loans provided to customers and employees."), - }, - - ] - }, - { - "label": _("Loan Security"), - "items": [ - { - "type": "doctype", - "name": "Loan Security Type", - }, - { - "type": "doctype", - "name": "Loan Security Price", - }, - { - "type": "doctype", - "name": "Loan Security", - }, - { - "type": "doctype", - "name": "Loan Security Pledge", - }, - { - "type": "doctype", - "name": "Loan Security Unpledge", - }, - { - "type": "doctype", - "name": "Loan Security Shortfall", - }, - ] - }, - { - "label": _("Disbursement and Repayment"), - "items": [ - { - "type": "doctype", - "name": "Loan Disbursement", - }, - { - "type": "doctype", - "name": "Loan Repayment", - }, - { - "type": "doctype", - "name": "Loan Interest Accrual" - } - ] - }, - { - "label": _("Loan Processes"), - "items": [ - { - "type": "doctype", - "name": "Process Loan Security Shortfall", - }, - { - "type": "doctype", - "name": "Process Loan Interest Accrual", - } - ] - }, - { - "label": _("Reports"), - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Loan Repayment and Closure", - "route": "#query-report/Loan Repayment and Closure", - "doctype": "Loan Repayment", - }, - { - "type": "report", - "is_query_report": True, - "name": "Loan Security Status", - "route": "#query-report/Loan Security Status", - "doctype": "Loan Security Pledge", - } - ] - } - ] \ No newline at end of file diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py deleted file mode 100644 index 012f1cad0a..0000000000 --- a/erpnext/config/manufacturing.py +++ /dev/null @@ -1,168 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Bill of Materials"), - "items": [ - { - "type": "doctype", - "name": "Item", - "description": _("All Products or Services."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "BOM", - "description": _("Bill of Materials (BOM)"), - "label": _("Bill of Materials"), - "onboard": 1, - "dependencies": ["Item"] - }, - { - "type": "doctype", - "name": "BOM Browser", - "icon": "fa fa-sitemap", - "label": _("BOM Browser"), - "description": _("Tree of Bill of Materials"), - "link": "Tree/BOM", - "onboard": 1, - "dependencies": ["Item"] - }, - - { - "type": "doctype", - "name": "Workstation", - "description": _("Where manufacturing operations are carried."), - }, - { - "type": "doctype", - "name": "Operation", - "description": _("Details of the operations carried out."), - }, - { - "type": "doctype", - "name": "Routing" - } - - ] - }, - { - "label": _("Production"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Work Order", - "description": _("Orders released for production."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Production Plan", - "description": _("Generate Material Requests (MRP) and Work Orders."), - "onboard": 1, - "dependencies": ["Item", "BOM"] - }, - { - "type": "doctype", - "name": "Stock Entry", - "onboard": 1, - "dependencies": ["Item"] - }, - { - "type": "doctype", - "name": "Timesheet", - "description": _("Time Sheet for manufacturing."), - "onboard": 1, - "dependencies": ["Activity Type"] - }, - { - "type": "doctype", - "name": "Job Card" - } - ] - }, - { - "label": _("Tools"), - "icon": "fa fa-wrench", - "items": [ - { - "type": "doctype", - "name": "BOM Update Tool", - "description": _("Replace BOM and update latest price in all BOMs"), - }, - { - "type": "page", - "label": _("BOM Comparison Tool"), - "name": "bom-comparison-tool", - "description": _("Compare BOMs for changes in Raw Materials and Operations"), - "data_doctype": "BOM" - }, - ] - }, - { - "label": _("Settings"), - "items": [ - { - "type": "doctype", - "name": "Manufacturing Settings", - "description": _("Global settings for all manufacturing processes."), - } - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Work Order Summary", - "doctype": "Work Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Issued Items Against Work Order", - "doctype": "Work Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Production Analytics", - "doctype": "Work Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "BOM Search", - "doctype": "BOM" - }, - { - "type": "report", - "is_query_report": True, - "name": "BOM Stock Report", - "doctype": "BOM" - } - ] - }, - { - "label": _("Help"), - "icon": "fa fa-facetime-video", - "items": [ - { - "type": "help", - "label": _("Bill of Materials"), - "youtube_id": "hDV0c1OeWLo" - }, - { - "type": "help", - "label": _("Work Order"), - "youtube_id": "ZotgLyp2YFY" - }, - ] - } - ] diff --git a/erpnext/config/non_profit.py b/erpnext/config/non_profit.py deleted file mode 100644 index 42ec9d3db3..0000000000 --- a/erpnext/config/non_profit.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Chapter"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Chapter", - "description": _("Chapter information."), - "onboard": 1, - } - ] - }, - { - "label": _("Membership"), - "items": [ - { - "type": "doctype", - "name": "Member", - "description": _("Member information."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Membership", - "description": _("Memebership Details"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Membership Type", - "description": _("Memebership Type Details"), - }, - ] - }, - { - "label": _("Volunteer"), - "items": [ - { - "type": "doctype", - "name": "Volunteer", - "description": _("Volunteer information."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Volunteer Type", - "description": _("Volunteer Type information."), - } - ] - }, - { - "label": _("Donor"), - "items": [ - { - "type": "doctype", - "name": "Donor", - "description": _("Donor information."), - }, - { - "type": "doctype", - "name": "Donor Type", - "description": _("Donor Type information."), - } - ] - }, - { - "label": _("Loan Management"), - "icon": "icon-list", - "items": [ - { - "type": "doctype", - "name": "Loan Type", - "description": _("Define various loan types") - }, - { - "type": "doctype", - "name": "Loan Application", - "description": _("Loan Application") - }, - { - "type": "doctype", - "name": "Loan" - }, - ] - }, - { - "label": _("Grant Application"), - "items": [ - { - "type": "doctype", - "name": "Grant Application", - "description": _("Grant information."), - } - ] - } - ] diff --git a/erpnext/config/quality_management.py b/erpnext/config/quality_management.py deleted file mode 100644 index 35acdfab24..0000000000 --- a/erpnext/config/quality_management.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Goal and Procedure"), - "items": [ - { - "type": "doctype", - "name": "Quality Goal", - "description":_("Quality Goal."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quality Procedure", - "description":_("Quality Procedure."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quality Procedure", - "icon": "fa fa-sitemap", - "label": _("Tree of Procedures"), - "route": "#Tree/Quality Procedure", - "description": _("Tree of Quality Procedures."), - }, - ] - }, - { - "label": _("Review and Action"), - "items": [ - { - "type": "doctype", - "name": "Quality Review", - "description":_("Quality Review"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quality Action", - "description":_("Quality Action"), - } - ] - }, - { - "label": _("Meeting"), - "items": [ - { - "type": "doctype", - "name": "Quality Meeting", - "description":_("Quality Meeting"), - } - ] - }, - { - "label": _("Feedback"), - "items": [ - { - "type": "doctype", - "name": "Quality Feedback", - "description":_("Quality Feedback"), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quality Feedback Template", - "description":_("Quality Feedback Template"), - } - ] - }, - ] \ No newline at end of file diff --git a/erpnext/config/retail.py b/erpnext/config/retail.py deleted file mode 100644 index 738be7eb17..0000000000 --- a/erpnext/config/retail.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Retail Operations"), - "items": [ - { - "type": "doctype", - "name": "POS Profile", - "label": _("Point-of-Sale Profile"), - "description": _("Setup default values for POS Invoices"), - "onboard": 1, - }, - { - "type": "page", - "name": "pos", - "label": _("POS"), - "description": _("Point of Sale"), - "onboard": 1, - "dependencies": ["POS Profile"] - }, - { - "type": "doctype", - "name": "Cashier Closing", - "description": _("Cashier Closing"), - }, - { - "type": "doctype", - "name": "POS Settings", - "description": _("Setup mode of POS (Online / Offline)") - }, - { - "type": "doctype", - "name": "Loyalty Program", - "label": _("Loyalty Program"), - "description": _("To make Customer based incentive schemes.") - }, - { - "type": "doctype", - "name": "Loyalty Point Entry", - "label": _("Loyalty Point Entry"), - "description": _("To view logs of Loyalty Points assigned to a Customer.") - } - ] - } - ] \ No newline at end of file diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py deleted file mode 100644 index 5db4cc2702..0000000000 --- a/erpnext/config/selling.py +++ /dev/null @@ -1,320 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Sales"), - "icon": "fa fa-star", - "items": [ - { - "type": "doctype", - "name": "Customer", - "description": _("Customer Database."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quotation", - "description": _("Quotes to Leads or Customers."), - "onboard": 1, - "dependencies": ["Item", "Customer"], - }, - { - "type": "doctype", - "name": "Sales Order", - "description": _("Confirmed orders from Customers."), - "onboard": 1, - "dependencies": ["Item", "Customer"], - }, - { - "type": "doctype", - "name": "Sales Invoice", - "description": _("Invoices for Costumers."), - "onboard": 1, - "dependencies": ["Item", "Customer"], - }, - { - "type": "doctype", - "name": "Blanket Order", - "description": _("Blanket Orders from Costumers."), - "onboard": 1, - "dependencies": ["Item", "Customer"], - }, - { - "type": "doctype", - "name": "Sales Partner", - "description": _("Manage Sales Partners."), - "dependencies": ["Item"], - }, - { - "type": "doctype", - "label": _("Sales Person"), - "name": "Sales Person", - "icon": "fa fa-sitemap", - "link": "Tree/Sales Person", - "description": _("Manage Sales Person Tree."), - "dependencies": ["Item", "Customer"], - }, - { - "type": "report", - "is_query_report": True, - "name": "Territory Target Variance (Item Group-Wise)", - "route": "#query-report/Territory Target Variance Item Group-Wise", - "doctype": "Territory", - }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Person Target Variance (Item Group-Wise)", - "route": "#query-report/Sales Person Target Variance Item Group-Wise", - "doctype": "Sales Person", - "dependencies": ["Sales Person"], - }, - ] - }, - { - "label": _("Items and Pricing"), - "items": [ - { - "type": "doctype", - "name": "Item", - "description": _("All Products or Services."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Item Price", - "description": _("Multiple Item prices."), - "route": "#Report/Item Price", - "dependencies": ["Item", "Price List"], - "onboard": 1, - }, - { - "type": "doctype", - "name": "Price List", - "description": _("Price List master."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Item Group", - "icon": "fa fa-sitemap", - "label": _("Item Group"), - "link": "Tree/Item Group", - "description": _("Tree of Item Groups."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Product Bundle", - "description": _("Bundle items at time of sale."), - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Promotional Scheme", - "description": _("Rules for applying different promotional schemes.") - }, - { - "type": "doctype", - "name": "Pricing Rule", - "description": _("Rules for applying pricing and discount."), - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Shipping Rule", - "description": _("Rules for adding shipping costs."), - }, - { - "type": "doctype", - "name": "Coupon Code", - "description": _("Define coupon codes."), - } - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "Selling Settings", - "description": _("Default settings for selling transactions."), - "settings": 1, - }, - { - "type": "doctype", - "name":"Terms and Conditions", - "label": _("Terms and Conditions Template"), - "description": _("Template of terms or contract."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Sales Taxes and Charges Template", - "description": _("Tax template for selling transactions."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Lead Source", - "description": _("Track Leads by Lead Source.") - }, - { - "type": "doctype", - "label": _("Customer Group"), - "name": "Customer Group", - "icon": "fa fa-sitemap", - "link": "Tree/Customer Group", - "description": _("Manage Customer Group Tree."), - }, - { - "type": "doctype", - "name": "Contact", - "description": _("All Contacts."), - }, - { - "type": "doctype", - "name": "Address", - "description": _("All Addresses."), - }, - { - "type": "doctype", - "label": _("Territory"), - "name": "Territory", - "icon": "fa fa-sitemap", - "link": "Tree/Territory", - "description": _("Manage Territory Tree."), - }, - { - "type": "doctype", - "name": "Campaign", - "description": _("Sales campaigns."), - }, - ] - }, - { - "label": _("Key Reports"), - "icon": "fa fa-table", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Sales Analytics", - "doctype": "Sales Order", - "onboard": 1, - }, - { - "type": "page", - "name": "sales-funnel", - "label": _("Sales Funnel"), - "icon": "fa fa-bar-chart", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Customer Acquisition and Loyalty", - "doctype": "Customer", - "icon": "fa fa-bar-chart", - }, - { - "type": "report", - "is_query_report": True, - "name": "Inactive Customers", - "doctype": "Sales Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Ordered Items To Be Delivered", - "doctype": "Sales Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Person-wise Transaction Summary", - "doctype": "Sales Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Item-wise Sales History", - "doctype": "Item" - }, - { - "type": "report", - "is_query_report": True, - "name": "Quotation Trends", - "doctype": "Quotation" - }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Order Trends", - "doctype": "Sales Order" - }, - ] - }, - { - "label": _("Other Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Lead Details", - "doctype": "Lead" - }, - { - "type": "report", - "is_query_report": True, - "name": "Address And Contacts", - "label": _("Customer Addresses And Contacts"), - "doctype": "Address", - "route_options": { - "party_type": "Customer" - } - }, - { - "type": "report", - "is_query_report": True, - "name": "BOM Search", - "doctype": "BOM" - }, - { - "type": "report", - "is_query_report": True, - "name": "Available Stock for Packing Items", - "doctype": "Item", - }, - { - "type": "report", - "is_query_report": True, - "name": "Pending SO Items For Purchase Request", - "doctype": "Sales Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Customer Credit Balance", - "doctype": "Customer" - }, - { - "type": "report", - "is_query_report": True, - "name": "Customers Without Any Sales Transactions", - "doctype": "Customer" - }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Partners Commission", - "doctype": "Customer" - } - ] - }, - - ] diff --git a/erpnext/config/settings.py b/erpnext/config/settings.py deleted file mode 100644 index 323683a3e6..0000000000 --- a/erpnext/config/settings.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -from frappe.desk.moduleview import add_setup_section - -def get_data(): - data = [ - { - "label": _("Settings"), - "icon": "fa fa-wrench", - "items": [ - { - "type": "doctype", - "name": "Global Defaults", - "label": _("ERPNext Settings"), - "description": _("Set Default Values like Company, Currency, Current Fiscal Year, etc."), - "hide_count": True, - "settings": 1, - } - ] - }, - { - "label": _("Printing"), - "icon": "fa fa-print", - "items": [ - { - "type": "doctype", - "name": "Letter Head", - "description": _("Letter Heads for print templates."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Print Heading", - "description": _("Titles for print templates e.g. Proforma Invoice.") - }, - { - "type": "doctype", - "name": "Address Template", - "description": _("Country wise default Address Templates") - }, - { - "type": "doctype", - "name": "Terms and Conditions", - "description": _("Standard contract terms for Sales or Purchase.") - }, - ] - }, - { - "label": _("Help"), - "items": [ - { - "type": "help", - "name": _("Data Import and Export"), - "youtube_id": "6wiriRKPhmg" - }, - { - "type": "help", - "label": _("Setting up Email"), - "youtube_id": "YFYe0DrB95o" - }, - { - "type": "help", - "label": _("Printing and Branding"), - "youtube_id": "cKZHcx1znMc" - }, - { - "type": "help", - "label": _("Users and Permissions"), - "youtube_id": "8Slw1hsTmUI" - }, - { - "type": "help", - "label": _("Workflow"), - "youtube_id": "yObJUg9FxFs" - }, - ] - }, - { - "label": _("Customize"), - "icon": "fa fa-glass", - "items": [ - { - "type": "doctype", - "name": "Authorization Rule", - "description": _("Create rules to restrict transactions based on values.") - } - ] - }, - { - "label": _("Email"), - "icon": "fa fa-envelope", - "items": [ - { - "type": "doctype", - "name": "Email Digest", - "description": _("Create and manage daily, weekly and monthly email digests.") - }, - { - "type": "doctype", - "name": "SMS Settings", - "description": _("Setup SMS gateway settings") - }, - ] - } - ] - - for module, label, icon in ( - ("accounts", _("Accounting"), "fa fa-money"), - ("stock", _("Stock"), "fa fa-truck"), - ("selling", _("Selling"), "fa fa-tag"), - ("buying", _("Buying"), "fa fa-shopping-cart"), - ("hr", _("Human Resources"), "fa fa-group"), - ("support", _("Support"), "fa fa-phone")): - - add_setup_section(data, "erpnext", module, label, icon) - - return data diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py deleted file mode 100644 index dd35f5ab36..0000000000 --- a/erpnext/config/stock.py +++ /dev/null @@ -1,361 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Stock Transactions"), - "items": [ - { - "type": "doctype", - "name": "Stock Entry", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Delivery Note", - "onboard": 1, - "dependencies": ["Item", "Customer"], - }, - { - "type": "doctype", - "name": "Purchase Receipt", - "onboard": 1, - "dependencies": ["Item", "Supplier"], - }, - { - "type": "doctype", - "name": "Material Request", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Pick List", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Delivery Trip" - }, - ] - }, - { - "label": _("Stock Reports"), - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Stock Ledger", - "doctype": "Stock Ledger Entry", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "report", - "is_query_report": True, - "name": "Stock Balance", - "doctype": "Stock Ledger Entry", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "report", - "is_query_report": True, - "name": "Stock Projected Qty", - "doctype": "Item", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "page", - "name": "stock-balance", - "label": _("Stock Summary"), - "dependencies": ["Item"], - }, - { - "type": "report", - "is_query_report": True, - "name": "Stock Ageing", - "doctype": "Item", - "dependencies": ["Item"], - }, - { - "type": "report", - "is_query_report": True, - "name": "Item Price Stock", - "doctype": "Item", - "dependencies": ["Item"], - } - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "Stock Settings", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Warehouse", - "onboard": 1, - }, - { - "type": "doctype", - "name": "UOM", - "label": _("Unit of Measure") + " (UOM)", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Brand", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Item Attribute", - }, - { - "type": "doctype", - "name": "Item Variant Settings", - }, - ] - }, - { - "label": _("Items and Pricing"), - "items": [ - { - "type": "doctype", - "name": "Item", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Product Bundle", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Item Group", - "icon": "fa fa-sitemap", - "label": _("Item Group"), - "link": "Tree/Item Group", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Price List", - }, - { - "type": "doctype", - "name": "Item Price", - }, - { - "type": "doctype", - "name": "Shipping Rule", - }, - { - "type": "doctype", - "name": "Pricing Rule", - }, - { - "type": "doctype", - "name": "Item Alternative", - }, - { - "type": "doctype", - "name": "Item Manufacturer", - }, - { - "type": "doctype", - "name": "Item Variant Settings", - }, - ] - }, - { - "label": _("Serial No and Batch"), - "items": [ - { - "type": "doctype", - "name": "Serial No", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Batch", - "onboard": 1, - "dependencies": ["Item"], - }, - { - "type": "doctype", - "name": "Installation Note", - "dependencies": ["Item"], - }, - { - "type": "report", - "name": "Serial No Service Contract Expiry", - "doctype": "Serial No" - }, - { - "type": "report", - "name": "Serial No Status", - "doctype": "Serial No" - }, - { - "type": "report", - "name": "Serial No Warranty Expiry", - "doctype": "Serial No" - }, - ] - }, - { - "label": _("Tools"), - "icon": "fa fa-wrench", - "items": [ - { - "type": "doctype", - "name": "Stock Reconciliation", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Landed Cost Voucher", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Packing Slip", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Quality Inspection", - }, - { - "type": "doctype", - "name": "Quality Inspection Template", - }, - { - "type": "doctype", - "name": "Quick Stock Balance", - }, - ] - }, - { - "label": _("Key Reports"), - "icon": "fa fa-table", - "items": [ - { - "type": "report", - "is_query_report": False, - "name": "Item-wise Price List Rate", - "doctype": "Item Price", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Stock Analytics", - "doctype": "Stock Entry", - "onboard": 1, - }, - { - "type": "report", - "is_query_report": True, - "name": "Delivery Note Trends", - "doctype": "Delivery Note" - }, - { - "type": "report", - "is_query_report": True, - "name": "Purchase Receipt Trends", - "doctype": "Purchase Receipt" - }, - { - "type": "report", - "is_query_report": True, - "name": "Ordered Items To Be Delivered", - "doctype": "Delivery Note" - }, - { - "type": "report", - "is_query_report": True, - "name": "Purchase Order Items To Be Received", - "doctype": "Purchase Receipt" - }, - { - "type": "report", - "is_query_report": True, - "name": "Item Shortage Report", - "doctype": "Bin" - }, - { - "type": "report", - "is_query_report": True, - "name": "Batch-Wise Balance History", - "doctype": "Batch" - }, - ] - }, - { - "label": _("Other Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Requested Items To Be Transferred", - "doctype": "Material Request" - }, - { - "type": "report", - "is_query_report": True, - "name": "Batch Item Expiry Status", - "doctype": "Stock Ledger Entry" - }, - { - "type": "report", - "is_query_report": True, - "name": "Item Prices", - "doctype": "Price List" - }, - { - "type": "report", - "is_query_report": True, - "name": "Itemwise Recommended Reorder Level", - "doctype": "Item" - }, - { - "type": "report", - "is_query_report": True, - "name": "Item Variant Details", - "doctype": "Item" - }, - { - "type": "report", - "is_query_report": True, - "name": "Subcontracted Raw Materials To Be Transferred", - "doctype": "Purchase Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Subcontracted Item To Be Received", - "doctype": "Purchase Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Stock and Account Value Comparison", - "doctype": "Stock Ledger Entry" - } - ] - }, - - ] diff --git a/erpnext/config/support.py b/erpnext/config/support.py deleted file mode 100644 index 151c4f743e..0000000000 --- a/erpnext/config/support.py +++ /dev/null @@ -1,105 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Issues"), - "items": [ - { - "type": "doctype", - "name": "Issue", - "description": _("Support queries from customers."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Issue Type", - "description": _("Issue Type."), - }, - { - "type": "doctype", - "name": "Issue Priority", - "description": _("Issue Priority."), - } - ] - }, - { - "label": _("Warranty"), - "items": [ - { - "type": "doctype", - "name": "Warranty Claim", - "description": _("Warranty Claim against Serial No."), - }, - { - "type": "doctype", - "name": "Serial No", - "description": _("Single unit of an Item."), - }, - ] - }, - { - "label": _("Service Level Agreement"), - "items": [ - { - "type": "doctype", - "name": "Service Level", - "description": _("Service Level."), - }, - { - "type": "doctype", - "name": "Service Level Agreement", - "description": _("Service Level Agreement."), - } - ] - }, - { - "label": _("Maintenance"), - "items": [ - { - "type": "doctype", - "name": "Maintenance Schedule", - }, - { - "type": "doctype", - "name": "Maintenance Visit", - }, - ] - }, - { - "label": _("Reports"), - "icon": "fa fa-list", - "items": [ - { - "type": "page", - "name": "support-analytics", - "label": _("Support Analytics"), - "icon": "fa fa-bar-chart" - }, - { - "type": "report", - "name": "Minutes to First Response for Issues", - "doctype": "Issue", - "is_query_report": True - }, - { - "type": "report", - "name": "Support Hours", - "doctype": "Issue", - "is_query_report": True - }, - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-list", - "items": [ - { - "type": "doctype", - "name": "Support Settings", - "label": _("Support Settings"), - }, - ] - }, - ] \ No newline at end of file diff --git a/erpnext/config/website.py b/erpnext/config/website.py deleted file mode 100644 index d31b057881..0000000000 --- a/erpnext/config/website.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return [ - { - "label": _("Portal"), - "items": [ - { - "type": "doctype", - "name": "Homepage", - "description": _("Settings for website homepage"), - }, - { - "type": "doctype", - "name": "Homepage Section", - "description": _("Add cards or custom sections on homepage"), - }, - { - "type": "doctype", - "name": "Products Settings", - "description": _("Settings for website product listing"), - }, - { - "type": "doctype", - "name": "Shopping Cart Settings", - "label": _("Shopping Cart Settings"), - "description": _("Settings for online shopping cart such as shipping rules, price list etc."), - "hide_count": True - } - ] - } - ] From 27e7c264f2d10e8851312742bf72b5beebb8b79c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 17 Nov 2020 13:19:21 +0530 Subject: [PATCH 189/283] fix: link type --- .../desk_page/accounting/accounting.json | 188 +++++++++--------- .../desk_page/agriculture/agriculture.json | 24 +-- erpnext/assets/desk_page/assets/assets.json | 26 +-- erpnext/buying/desk_page/buying/buying.json | 80 ++++---- erpnext/crm/desk_page/crm/crm.json | 64 +++--- .../desk_page/education/education.json | 106 +++++----- .../erpnext_integrations.json | 16 +- .../erpnext_integrations_settings.json | 12 +- .../desk_page/healthcare/healthcare.json | 82 ++++---- erpnext/hr/desk_page/hr/hr.json | 156 +++++++-------- .../loan_management/desk_page/loan/loan.json | 36 ++-- .../manufacturing/manufacturing.json | 46 ++--- .../desk_page/non_profit/non_profit.json | 28 +-- .../payroll/desk_page/payroll/payroll.json | 50 ++--- .../projects/desk_page/projects/projects.json | 24 +-- .../desk_page/quality/quality.json | 20 +- erpnext/selling/desk_page/retail/retail.json | 14 +- .../selling/desk_page/selling/selling.json | 94 ++++----- .../erpnext_settings/erpnext_settings.json | 2 +- erpnext/setup/desk_page/home/home.json | 70 +++---- erpnext/stock/desk_page/stock/stock.json | 118 +++++------ .../support/desk_page/support/support.json | 22 +- .../desk_page/utilities/utilities.json | 6 +- 23 files changed, 642 insertions(+), 642 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index dfc2243b38..2de2492d2a 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -114,7 +114,7 @@ "link_to": "Company", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -123,7 +123,7 @@ "link_to": "Account", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -132,7 +132,7 @@ "link_to": "Accounts Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -141,7 +141,7 @@ "link_to": "Fiscal Year", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -150,7 +150,7 @@ "link_to": "Accounting Dimension", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -159,7 +159,7 @@ "link_to": "Finance Book", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -168,7 +168,7 @@ "link_to": "Accounting Period", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -177,7 +177,7 @@ "link_to": "Payment Term", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -193,7 +193,7 @@ "link_to": "Journal Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -202,7 +202,7 @@ "link_to": "Journal Entry Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -211,7 +211,7 @@ "link_to": "General Ledger", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -220,7 +220,7 @@ "link_to": "Customer Ledger Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -229,7 +229,7 @@ "link_to": "Supplier Ledger Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -245,7 +245,7 @@ "link_to": "Sales Invoice", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -254,7 +254,7 @@ "link_to": "Customer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -263,7 +263,7 @@ "link_to": "Payment Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -272,7 +272,7 @@ "link_to": "Payment Request", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -281,7 +281,7 @@ "link_to": "Accounts Receivable", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -290,7 +290,7 @@ "link_to": "Accounts Receivable Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -299,7 +299,7 @@ "link_to": "Sales Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -308,7 +308,7 @@ "link_to": "Item-wise Sales Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -317,7 +317,7 @@ "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -326,7 +326,7 @@ "link_to": "Delivered Items To Be Billed", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -342,7 +342,7 @@ "link_to": "Purchase Invoice", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -351,7 +351,7 @@ "link_to": "Supplier", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -360,7 +360,7 @@ "link_to": "Payment Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -369,7 +369,7 @@ "link_to": "Accounts Payable", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -378,7 +378,7 @@ "link_to": "Accounts Payable Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -387,7 +387,7 @@ "link_to": "Purchase Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -396,7 +396,7 @@ "link_to": "Item-wise Purchase Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -405,7 +405,7 @@ "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -414,7 +414,7 @@ "link_to": "Received Items To Be Billed", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -430,7 +430,7 @@ "link_to": "Trial Balance for Party", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -439,7 +439,7 @@ "link_to": "Payment Period Based On Invoice Date", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -448,7 +448,7 @@ "link_to": "Sales Partners Commission", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -457,7 +457,7 @@ "link_to": "Customer Credit Balance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -466,7 +466,7 @@ "link_to": "Sales Payment Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -475,7 +475,7 @@ "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -484,7 +484,7 @@ "link_to": "DATEV", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -500,7 +500,7 @@ "link_to": "Trial Balance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -509,7 +509,7 @@ "link_to": "Profit and Loss Statement", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -518,7 +518,7 @@ "link_to": "Balance Sheet", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -527,7 +527,7 @@ "link_to": "Cash Flow", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -536,7 +536,7 @@ "link_to": "Consolidated Financial Statement", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -552,7 +552,7 @@ "link_to": "Currency", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -561,7 +561,7 @@ "link_to": "Currency Exchange", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -570,7 +570,7 @@ "link_to": "Exchange Rate Revaluation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -586,7 +586,7 @@ "link_to": "Payment Gateway Account", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -595,7 +595,7 @@ "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -604,7 +604,7 @@ "link_to": "Mode of Payment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -620,7 +620,7 @@ "link_to": "Bank", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -629,7 +629,7 @@ "link_to": "Bank Account", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -638,7 +638,7 @@ "link_to": "Bank Clearance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -647,7 +647,7 @@ "link_to": "bank-reconciliation", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -656,7 +656,7 @@ "link_to": "Bank Reconciliation Statement", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -665,7 +665,7 @@ "link_to": "Bank Statement Transaction Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -674,7 +674,7 @@ "link_to": "Bank Statement Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -690,7 +690,7 @@ "link_to": "Subscription Plan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -699,7 +699,7 @@ "link_to": "Subscription", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -708,7 +708,7 @@ "link_to": "Subscription Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -724,7 +724,7 @@ "link_to": "GST Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -733,7 +733,7 @@ "link_to": "GST HSN Code", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -742,7 +742,7 @@ "link_to": "GSTR-1", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -751,7 +751,7 @@ "link_to": "GSTR-2", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -760,7 +760,7 @@ "link_to": "GSTR 3B Report", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -769,7 +769,7 @@ "link_to": "GST Sales Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -778,7 +778,7 @@ "link_to": "GST Purchase Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -787,7 +787,7 @@ "link_to": "GST Itemised Sales Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -796,7 +796,7 @@ "link_to": "GST Itemised Purchase Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -805,7 +805,7 @@ "link_to": "C-Form", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -814,7 +814,7 @@ "link_to": "Lower Deduction Certificate", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -830,7 +830,7 @@ "link_to": "Shareholder", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -839,7 +839,7 @@ "link_to": "Share Transfer", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -848,7 +848,7 @@ "link_to": "Share Ledger", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -857,7 +857,7 @@ "link_to": "Share Balance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -873,7 +873,7 @@ "link_to": "Cost Center", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -882,7 +882,7 @@ "link_to": "Budget", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -891,7 +891,7 @@ "link_to": "Accounting Dimension", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -900,7 +900,7 @@ "link_to": "Budget Variance Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -909,7 +909,7 @@ "link_to": "Monthly Distribution", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -925,7 +925,7 @@ "link_to": "Opening Invoice Creation Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -934,7 +934,7 @@ "link_to": "Chart of Accounts Importer", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -943,7 +943,7 @@ "link_to": "Period Closing Voucher", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -959,7 +959,7 @@ "link_to": "Sales Taxes and Charges Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -968,7 +968,7 @@ "link_to": "Purchase Taxes and Charges Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -977,7 +977,7 @@ "link_to": "Item Tax Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -986,7 +986,7 @@ "link_to": "Tax Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -995,7 +995,7 @@ "link_to": "Tax Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -1004,7 +1004,7 @@ "link_to": "Tax Withholding Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -1020,7 +1020,7 @@ "link_to": "Gross Profit", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -1029,7 +1029,7 @@ "link_to": "Profitability Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -1038,7 +1038,7 @@ "link_to": "Sales Invoice Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -1047,10 +1047,10 @@ "link_to": "Purchase Invoice Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:30.922133", + "modified": "2020-11-17 13:18:37.583879", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 30ac23d60f..145ef07c10 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -44,7 +44,7 @@ "link_to": "Crop", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -53,7 +53,7 @@ "link_to": "Crop Cycle", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -62,7 +62,7 @@ "link_to": "Location", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -78,7 +78,7 @@ "link_to": "Plant Analysis", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -87,7 +87,7 @@ "link_to": "Soil Analysis", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -96,7 +96,7 @@ "link_to": "Water Analysis", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -105,7 +105,7 @@ "link_to": "Soil Texture", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -114,7 +114,7 @@ "link_to": "Weather", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -123,7 +123,7 @@ "link_to": "Agriculture Analysis Criteria", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -139,7 +139,7 @@ "link_to": "Disease", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -148,10 +148,10 @@ "link_to": "Fertilizer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:25.889184", + "modified": "2020-11-17 13:18:42.501139", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index f5330c083f..66f448fc9c 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -49,7 +49,7 @@ "link_to": "Asset", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -58,7 +58,7 @@ "link_to": "Location", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -67,7 +67,7 @@ "link_to": "Asset Category", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -76,7 +76,7 @@ "link_to": "Asset Movement", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -92,7 +92,7 @@ "link_to": "Asset Maintenance Team", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -101,7 +101,7 @@ "link_to": "Asset Maintenance", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -110,7 +110,7 @@ "link_to": "Asset Maintenance Log", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -119,7 +119,7 @@ "link_to": "Asset Value Adjustment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -128,7 +128,7 @@ "link_to": "Asset Repair", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -144,7 +144,7 @@ "link_to": "Asset Depreciation Ledger", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -153,7 +153,7 @@ "link_to": "Asset Depreciations and Balances", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -162,10 +162,10 @@ "link_to": "Asset Maintenance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:26.576154", + "modified": "2020-11-17 13:18:41.714472", "modified_by": "Administrator", "module": "Assets", "name": "Assets", diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 1c1d8f38d8..5b85e6ab10 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -76,7 +76,7 @@ "link_to": "Material Request", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -85,7 +85,7 @@ "link_to": "Purchase Order", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -94,7 +94,7 @@ "link_to": "Purchase Invoice", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -103,7 +103,7 @@ "link_to": "Request for Quotation", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -112,7 +112,7 @@ "link_to": "Supplier Quotation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -128,7 +128,7 @@ "link_to": "Item", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -137,7 +137,7 @@ "link_to": "Item Price", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -146,7 +146,7 @@ "link_to": "Price List", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -155,7 +155,7 @@ "link_to": "Product Bundle", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -164,7 +164,7 @@ "link_to": "Item Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -173,7 +173,7 @@ "link_to": "Promotional Scheme", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -182,7 +182,7 @@ "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -198,7 +198,7 @@ "link_to": "Buying Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -207,7 +207,7 @@ "link_to": "Purchase Taxes and Charges Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -216,7 +216,7 @@ "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -232,7 +232,7 @@ "link_to": "Supplier", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -241,7 +241,7 @@ "link_to": "Supplier Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -250,7 +250,7 @@ "link_to": "Contact", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -259,7 +259,7 @@ "link_to": "Address", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -275,7 +275,7 @@ "link_to": "Supplier Scorecard", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -284,7 +284,7 @@ "link_to": "Supplier Scorecard Variable", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -293,7 +293,7 @@ "link_to": "Supplier Scorecard Criteria", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -302,7 +302,7 @@ "link_to": "Supplier Scorecard Standing", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -318,7 +318,7 @@ "link_to": "Purchase Analytics", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -327,7 +327,7 @@ "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -336,7 +336,7 @@ "link_to": "Supplier-Wise Sales Analytics", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -345,7 +345,7 @@ "link_to": "Requested Items to Order and Receive", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -354,7 +354,7 @@ "link_to": "Purchase Order Trends", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -363,7 +363,7 @@ "link_to": "Procurement Tracker", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -379,7 +379,7 @@ "link_to": "Items To Be Requested", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -388,7 +388,7 @@ "link_to": "Item-wise Purchase History", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -397,7 +397,7 @@ "link_to": "Purchase Receipt Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -406,7 +406,7 @@ "link_to": "Purchase Invoice Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -415,7 +415,7 @@ "link_to": "Subcontracted Raw Materials To Be Transferred", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -424,7 +424,7 @@ "link_to": "Subcontracted Item To Be Received", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -433,7 +433,7 @@ "link_to": "Supplier Quotation Comparison", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -442,7 +442,7 @@ "link_to": "Material Requests for which Supplier Quotations are not created", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -451,7 +451,7 @@ "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -467,10 +467,10 @@ "link_to": "Import Supplier Invoice", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:25.184766", + "modified": "2020-11-17 13:18:43.304970", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 3238962a28..fd97ba6c0c 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -58,7 +58,7 @@ "link_to": "Lead", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -67,7 +67,7 @@ "link_to": "Opportunity", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -76,7 +76,7 @@ "link_to": "Customer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -85,7 +85,7 @@ "link_to": "Contact", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -94,7 +94,7 @@ "link_to": "Communication", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -103,7 +103,7 @@ "link_to": "Lead Source", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -112,7 +112,7 @@ "link_to": "Contract", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -121,7 +121,7 @@ "link_to": "Appointment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -130,7 +130,7 @@ "link_to": "Newsletter", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -146,7 +146,7 @@ "link_to": "Lead Details", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -155,7 +155,7 @@ "link_to": "sales-funnel", "link_type": "Page", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -164,7 +164,7 @@ "link_to": "Prospects Engaged But Not Converted", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -173,7 +173,7 @@ "link_to": "First Response Time for Opportunity", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -182,7 +182,7 @@ "link_to": "Inactive Customers", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -191,7 +191,7 @@ "link_to": "Campaign Efficiency", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -200,7 +200,7 @@ "link_to": "Lead Owner Efficiency", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -216,7 +216,7 @@ "link_to": "Maintenance Schedule", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -225,7 +225,7 @@ "link_to": "Maintenance Visit", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -234,7 +234,7 @@ "link_to": "Warranty Claim", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -250,7 +250,7 @@ "link_to": "Campaign", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -259,7 +259,7 @@ "link_to": "Email Campaign", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -268,7 +268,7 @@ "link_to": "Social Media Post", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -284,7 +284,7 @@ "link_to": "Customer Group", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -293,7 +293,7 @@ "link_to": "Territory", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -302,7 +302,7 @@ "link_to": "Sales Person", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -311,7 +311,7 @@ "link_to": "SMS Center", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -320,7 +320,7 @@ "link_to": "SMS Log", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -329,7 +329,7 @@ "link_to": "SMS Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -338,7 +338,7 @@ "link_to": "Email Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -347,7 +347,7 @@ "link_to": "Twitter Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -356,10 +356,10 @@ "link_to": "LinkedIn Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:28.196723", + "modified": "2020-11-17 13:18:40.216301", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index dd5338926d..06d7797933 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -99,7 +99,7 @@ "link_to": "Student", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -108,7 +108,7 @@ "link_to": "Instructor", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -117,7 +117,7 @@ "link_to": "Guardian", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -126,7 +126,7 @@ "link_to": "Student Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -135,7 +135,7 @@ "link_to": "Student Log", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -151,7 +151,7 @@ "link_to": "Program", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -160,7 +160,7 @@ "link_to": "Course", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -169,7 +169,7 @@ "link_to": "Topic", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -178,7 +178,7 @@ "link_to": "Room", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -194,7 +194,7 @@ "link_to": "Article", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -203,7 +203,7 @@ "link_to": "Video", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -212,7 +212,7 @@ "link_to": "Quiz", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -228,7 +228,7 @@ "link_to": "Education Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -237,7 +237,7 @@ "link_to": "Student Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -246,7 +246,7 @@ "link_to": "Student Batch Name", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -255,7 +255,7 @@ "link_to": "Grading Scale", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -264,7 +264,7 @@ "link_to": "Academic Term", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -273,7 +273,7 @@ "link_to": "Academic Year", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -289,7 +289,7 @@ "link_to": "Student Applicant", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -298,7 +298,7 @@ "link_to": "Student Admission", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -307,7 +307,7 @@ "link_to": "Program Enrollment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -316,7 +316,7 @@ "link_to": "Course Enrollment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -332,7 +332,7 @@ "link_to": "Fee Structure", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -341,7 +341,7 @@ "link_to": "Fee Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -350,7 +350,7 @@ "link_to": "Fee Schedule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -359,7 +359,7 @@ "link_to": "Fees", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -368,7 +368,7 @@ "link_to": "Student Fee Collection", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -377,7 +377,7 @@ "link_to": "Program wise Fee Collection", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -393,7 +393,7 @@ "link_to": "Course Schedule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -402,7 +402,7 @@ "link_to": "Course Scheduling Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -418,7 +418,7 @@ "link_to": "Student Attendance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -427,7 +427,7 @@ "link_to": "Student Leave Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -436,7 +436,7 @@ "link_to": "Student Monthly Attendance Sheet", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -445,7 +445,7 @@ "link_to": "Absent Student Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -454,7 +454,7 @@ "link_to": "Student Batch-Wise Attendance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -470,7 +470,7 @@ "link_to": "Course Enrollment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -479,7 +479,7 @@ "link_to": "Course Activity", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -488,7 +488,7 @@ "link_to": "Quiz Activity", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -504,7 +504,7 @@ "link_to": "Assessment Plan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -513,7 +513,7 @@ "link_to": "Assessment Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -522,7 +522,7 @@ "link_to": "Assessment Result", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -531,7 +531,7 @@ "link_to": "Assessment Criteria", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -547,7 +547,7 @@ "link_to": "Course wise Assessment Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -556,7 +556,7 @@ "link_to": "Final Assessment Grades", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -565,7 +565,7 @@ "link_to": "Assessment Plan Status", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -574,7 +574,7 @@ "link_to": "Student Report Generation Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -590,7 +590,7 @@ "link_to": "Student Attendance Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -599,7 +599,7 @@ "link_to": "Assessment Result Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -608,7 +608,7 @@ "link_to": "Student Group Creation Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -617,7 +617,7 @@ "link_to": "Program Enrollment Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -626,7 +626,7 @@ "link_to": "Course Scheduling Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -642,10 +642,10 @@ "link_to": "Student and Guardian Contact Details", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:27.243450", + "modified": "2020-11-17 13:18:40.981880", "modified_by": "Administrator", "module": "Education", "name": "Education", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json index cffa5a2e2c..df4ed76d47 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json @@ -44,7 +44,7 @@ "link_to": "Woocommerce Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -53,7 +53,7 @@ "link_to": "Amazon MWS Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -62,7 +62,7 @@ "link_to": "Shopify Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -78,7 +78,7 @@ "link_to": "GoCardless Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -87,7 +87,7 @@ "link_to": "Mpesa Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -103,7 +103,7 @@ "link_to": "Plaid Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -112,10 +112,10 @@ "link_to": "Exotel Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:30.437617", + "modified": "2020-11-17 13:18:38.150378", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json index fd5d4dc1c3..0150a0d530 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -34,7 +34,7 @@ "link_to": "Woocommerce Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -43,7 +43,7 @@ "link_to": "Shopify Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -52,7 +52,7 @@ "link_to": "Amazon MWS Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -61,7 +61,7 @@ "link_to": "Plaid Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -70,10 +70,10 @@ "link_to": "Exotel Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:31.919189", + "modified": "2020-11-17 13:18:36.389137", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations Settings", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 63751e3234..baf7ad86e6 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -80,7 +80,7 @@ "link_to": "Patient", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -89,7 +89,7 @@ "link_to": "Healthcare Practitioner", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -98,7 +98,7 @@ "link_to": "Practitioner Schedule", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -107,7 +107,7 @@ "link_to": "Medical Department", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -116,7 +116,7 @@ "link_to": "Healthcare Service Unit Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -125,7 +125,7 @@ "link_to": "Healthcare Service Unit", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -134,7 +134,7 @@ "link_to": "Medical Code Standard", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -143,7 +143,7 @@ "link_to": "Medical Code", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -159,7 +159,7 @@ "link_to": "Appointment Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -168,7 +168,7 @@ "link_to": "Clinical Procedure Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -177,7 +177,7 @@ "link_to": "Prescription Dosage", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -186,7 +186,7 @@ "link_to": "Prescription Duration", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -195,7 +195,7 @@ "link_to": "Antibiotic", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -211,7 +211,7 @@ "link_to": "Patient Appointment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -220,7 +220,7 @@ "link_to": "Clinical Procedure", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -229,7 +229,7 @@ "link_to": "Patient Encounter", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -238,7 +238,7 @@ "link_to": "Vital Signs", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -247,7 +247,7 @@ "link_to": "Complaint", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -256,7 +256,7 @@ "link_to": "Diagnosis", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -265,7 +265,7 @@ "link_to": "Fee Validity", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -281,7 +281,7 @@ "link_to": "Healthcare Settings", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -297,7 +297,7 @@ "link_to": "Lab Test Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -306,7 +306,7 @@ "link_to": "Lab Test Sample", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -315,7 +315,7 @@ "link_to": "Lab Test UOM", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -324,7 +324,7 @@ "link_to": "Sensitivity", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -340,7 +340,7 @@ "link_to": "Lab Test", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -349,7 +349,7 @@ "link_to": "Sample Collection", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -358,7 +358,7 @@ "link_to": "Dosage Form", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -374,7 +374,7 @@ "link_to": "Exercise Type", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -383,7 +383,7 @@ "link_to": "Therapy Type", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -392,7 +392,7 @@ "link_to": "Therapy Plan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -401,7 +401,7 @@ "link_to": "Therapy Session", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -410,7 +410,7 @@ "link_to": "Patient Assessment Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -419,7 +419,7 @@ "link_to": "Patient Assessment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -435,7 +435,7 @@ "link_to": "patient_history", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -444,7 +444,7 @@ "link_to": "patient-progress", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -453,7 +453,7 @@ "link_to": "Patient Medical Record", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -462,7 +462,7 @@ "link_to": "Inpatient Record", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -478,7 +478,7 @@ "link_to": "Patient Appointment Analytics", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -487,10 +487,10 @@ "link_to": "Lab Test Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:31.675127", + "modified": "2020-11-17 13:18:36.771243", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 4a2ff10a2a..787dbd35dd 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -109,7 +109,7 @@ "link_to": "Employee", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -118,7 +118,7 @@ "link_to": "Employment Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -127,7 +127,7 @@ "link_to": "Branch", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -136,7 +136,7 @@ "link_to": "Department", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -145,7 +145,7 @@ "link_to": "Designation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -154,7 +154,7 @@ "link_to": "Employee Grade", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -163,7 +163,7 @@ "link_to": "Employee Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -172,7 +172,7 @@ "link_to": "Employee Health Insurance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -188,7 +188,7 @@ "link_to": "Employee Onboarding", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -197,7 +197,7 @@ "link_to": "Employee Skill Map", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -206,7 +206,7 @@ "link_to": "Employee Promotion", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -215,7 +215,7 @@ "link_to": "Employee Transfer", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -224,7 +224,7 @@ "link_to": "Employee Separation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -233,7 +233,7 @@ "link_to": "Employee Onboarding Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -242,7 +242,7 @@ "link_to": "Employee Separation Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -258,7 +258,7 @@ "link_to": "Shift Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -267,7 +267,7 @@ "link_to": "Shift Request", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -276,7 +276,7 @@ "link_to": "Shift Assignment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -292,7 +292,7 @@ "link_to": "Leave Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -301,7 +301,7 @@ "link_to": "Leave Allocation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -310,7 +310,7 @@ "link_to": "Leave Policy", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -319,7 +319,7 @@ "link_to": "Leave Period", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -328,7 +328,7 @@ "link_to": "Leave Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -337,7 +337,7 @@ "link_to": "Holiday List", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -346,7 +346,7 @@ "link_to": "Compensatory Leave Request", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -355,7 +355,7 @@ "link_to": "Leave Encashment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -364,7 +364,7 @@ "link_to": "Leave Block List", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -373,7 +373,7 @@ "link_to": "Employee Leave Balance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -389,7 +389,7 @@ "link_to": "Salary Structure", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -398,7 +398,7 @@ "link_to": "Salary Structure Assignment", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -407,7 +407,7 @@ "link_to": "Payroll Entry", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -416,7 +416,7 @@ "link_to": "Salary Slip", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -425,7 +425,7 @@ "link_to": "Payroll Period", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -434,7 +434,7 @@ "link_to": "Income Tax Slab", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -443,7 +443,7 @@ "link_to": "Salary Component", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -452,7 +452,7 @@ "link_to": "Additional Salary", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -461,7 +461,7 @@ "link_to": "Retention Bonus", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -470,7 +470,7 @@ "link_to": "Employee Incentive", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -479,7 +479,7 @@ "link_to": "Salary Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -495,7 +495,7 @@ "link_to": "Employee Attendance Tool", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -504,7 +504,7 @@ "link_to": "Attendance", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -513,7 +513,7 @@ "link_to": "Attendance Request", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -522,7 +522,7 @@ "link_to": "Upload Attendance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -531,7 +531,7 @@ "link_to": "Employee Checkin", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -540,7 +540,7 @@ "link_to": "Monthly Attendance Sheet", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -556,7 +556,7 @@ "link_to": "Expense Claim", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -565,7 +565,7 @@ "link_to": "Employee Advance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -581,7 +581,7 @@ "link_to": "HR Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -590,7 +590,7 @@ "link_to": "Daily Work Summary Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -599,7 +599,7 @@ "link_to": "team-updates", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -615,7 +615,7 @@ "link_to": "Vehicle", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -624,7 +624,7 @@ "link_to": "Vehicle Log", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -633,7 +633,7 @@ "link_to": "Vehicle Expenses", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -649,7 +649,7 @@ "link_to": "Job Opening", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -658,7 +658,7 @@ "link_to": "Job Applicant", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -667,7 +667,7 @@ "link_to": "Job Offer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -676,7 +676,7 @@ "link_to": "Staffing Plan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -692,7 +692,7 @@ "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -701,7 +701,7 @@ "link_to": "Loan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -710,7 +710,7 @@ "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -726,7 +726,7 @@ "link_to": "Training Program", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -735,7 +735,7 @@ "link_to": "Training Event", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -744,7 +744,7 @@ "link_to": "Training Result", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -753,7 +753,7 @@ "link_to": "Training Feedback", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -769,7 +769,7 @@ "link_to": "Employee Birthday", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -778,7 +778,7 @@ "link_to": "Employees working on a holiday", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -794,7 +794,7 @@ "link_to": "Appraisal", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -803,7 +803,7 @@ "link_to": "Appraisal Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -812,7 +812,7 @@ "link_to": "Energy Point Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -821,7 +821,7 @@ "link_to": "Energy Point Log", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -837,7 +837,7 @@ "link_to": "Employee Tax Exemption Declaration", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -846,7 +846,7 @@ "link_to": "Employee Tax Exemption Proof Submission", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -855,7 +855,7 @@ "link_to": "Employee Other Income", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -864,7 +864,7 @@ "link_to": "Employee Benefit Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -873,7 +873,7 @@ "link_to": "Employee Benefit Claim", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -882,7 +882,7 @@ "link_to": "Employee Tax Exemption Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -891,10 +891,10 @@ "link_to": "Employee Tax Exemption Sub Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:24.707870", + "modified": "2020-11-17 13:18:43.807222", "modified_by": "Administrator", "module": "HR", "name": "HR", diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 0156809619..0c9bd358e9 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -54,7 +54,7 @@ "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -63,7 +63,7 @@ "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -72,7 +72,7 @@ "link_to": "Loan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -88,7 +88,7 @@ "link_to": "Process Loan Security Shortfall", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -97,7 +97,7 @@ "link_to": "Process Loan Interest Accrual", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -113,7 +113,7 @@ "link_to": "Loan Disbursement", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -122,7 +122,7 @@ "link_to": "Loan Repayment", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -131,7 +131,7 @@ "link_to": "Loan Write Off", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -140,7 +140,7 @@ "link_to": "Loan Interest Accrual", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -156,7 +156,7 @@ "link_to": "Loan Security Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -165,7 +165,7 @@ "link_to": "Loan Security Price", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -174,7 +174,7 @@ "link_to": "Loan Security", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -183,7 +183,7 @@ "link_to": "Loan Security Pledge", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -192,7 +192,7 @@ "link_to": "Loan Security Unpledge", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -201,7 +201,7 @@ "link_to": "Loan Security Shortfall", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -217,7 +217,7 @@ "link_to": "Loan Repayment and Closure", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -226,10 +226,10 @@ "link_to": "Loan Security Status", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:29.299238", + "modified": "2020-11-17 13:18:39.173544", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 020b147916..4eb6f2809f 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -58,7 +58,7 @@ "link_to": "Work Order", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -67,7 +67,7 @@ "link_to": "Production Plan", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -76,7 +76,7 @@ "link_to": "Stock Entry", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -85,7 +85,7 @@ "link_to": "Job Card", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -94,7 +94,7 @@ "link_to": "Downtime Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -110,7 +110,7 @@ "link_to": "Item", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -119,7 +119,7 @@ "link_to": "BOM", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -128,7 +128,7 @@ "link_to": "Workstation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -137,7 +137,7 @@ "link_to": "Operation", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -146,7 +146,7 @@ "link_to": "Routing", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -162,7 +162,7 @@ "link_to": "Production Planning Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -171,7 +171,7 @@ "link_to": "Work Order Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -180,7 +180,7 @@ "link_to": "Quality Inspection Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -189,7 +189,7 @@ "link_to": "Downtime Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -198,7 +198,7 @@ "link_to": "Job Card Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -207,7 +207,7 @@ "link_to": "BOM Search", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -216,7 +216,7 @@ "link_to": "BOM Stock Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -225,7 +225,7 @@ "link_to": "Production Analytics", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -234,7 +234,7 @@ "link_to": "BOM Operations Time", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -250,7 +250,7 @@ "link_to": "BOM Update Tool", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -259,7 +259,7 @@ "link_to": "bom-comparison-tool", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -275,10 +275,10 @@ "link_to": "Manufacturing Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:24.140642", + "modified": "2020-11-17 13:18:44.426761", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index 078e324454..4326250355 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -59,7 +59,7 @@ "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -68,7 +68,7 @@ "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -77,7 +77,7 @@ "link_to": "Loan", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -93,7 +93,7 @@ "link_to": "Grant Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -109,7 +109,7 @@ "link_to": "Member", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -118,7 +118,7 @@ "link_to": "Membership", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -127,7 +127,7 @@ "link_to": "Membership Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -136,7 +136,7 @@ "link_to": "Membership Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -152,7 +152,7 @@ "link_to": "Volunteer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -161,7 +161,7 @@ "link_to": "Volunteer Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -177,7 +177,7 @@ "link_to": "Chapter", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -193,7 +193,7 @@ "link_to": "Donor", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -202,10 +202,10 @@ "link_to": "Donor Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:26.036726", + "modified": "2020-11-17 13:18:42.271279", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index 9247213c34..0397055184 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -54,7 +54,7 @@ "link_to": "Salary Component", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -63,7 +63,7 @@ "link_to": "Salary Structure", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -72,7 +72,7 @@ "link_to": "Salary Structure Assignment", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -81,7 +81,7 @@ "link_to": "Payroll Entry", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -90,7 +90,7 @@ "link_to": "Salary Slip", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -106,7 +106,7 @@ "link_to": "Payroll Period", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -115,7 +115,7 @@ "link_to": "Income Tax Slab", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -124,7 +124,7 @@ "link_to": "Employee Other Income", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -133,7 +133,7 @@ "link_to": "Employee Tax Exemption Declaration", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -142,7 +142,7 @@ "link_to": "Employee Tax Exemption Proof Submission", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -151,7 +151,7 @@ "link_to": "Employee Tax Exemption Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -160,7 +160,7 @@ "link_to": "Employee Tax Exemption Sub Category", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -176,7 +176,7 @@ "link_to": "Additional Salary", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -185,7 +185,7 @@ "link_to": "Retention Bonus", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -194,7 +194,7 @@ "link_to": "Employee Incentive", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -203,7 +203,7 @@ "link_to": "Employee Benefit Application", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -212,7 +212,7 @@ "link_to": "Employee Benefit Claim", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -228,7 +228,7 @@ "link_to": "Salary Register", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -237,7 +237,7 @@ "link_to": "Salary Payments Based On Payment Mode", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -246,7 +246,7 @@ "link_to": "Salary Payments via ECS", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -255,7 +255,7 @@ "link_to": "Income Tax Deductions", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -264,7 +264,7 @@ "link_to": "Professional Tax Deductions", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -273,7 +273,7 @@ "link_to": "Provident Fund Deductions", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -282,10 +282,10 @@ "link_to": "Bank Remittance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:27.751514", + "modified": "2020-11-17 13:18:40.644642", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 4ea2963f50..7ad5398251 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -49,7 +49,7 @@ "link_to": "Project", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -58,7 +58,7 @@ "link_to": "Task", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -67,7 +67,7 @@ "link_to": "Project Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -76,7 +76,7 @@ "link_to": "Project Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -85,7 +85,7 @@ "link_to": "Project Update", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -101,7 +101,7 @@ "link_to": "Timesheet", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -110,7 +110,7 @@ "link_to": "Activity Type", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -119,7 +119,7 @@ "link_to": "Activity Cost", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -135,7 +135,7 @@ "link_to": "Daily Timesheet Summary", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -144,7 +144,7 @@ "link_to": "Project wise Stock Tracking", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -153,10 +153,10 @@ "link_to": "Project Billing Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:26.739225", + "modified": "2020-11-17 13:18:41.526145", "modified_by": "Administrator", "module": "Projects", "name": "Projects", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 499182e082..59a112ec77 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -49,7 +49,7 @@ "link_to": "Quality Goal", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -58,7 +58,7 @@ "link_to": "Quality Procedure", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -67,7 +67,7 @@ "link_to": "Quality Procedure", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -83,7 +83,7 @@ "link_to": "Quality Feedback", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -92,7 +92,7 @@ "link_to": "Quality Feedback Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -108,7 +108,7 @@ "link_to": "Quality Meeting", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -124,7 +124,7 @@ "link_to": "Non Conformance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -133,7 +133,7 @@ "link_to": "Quality Review", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -142,10 +142,10 @@ "link_to": "Quality Action", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:31.488858", + "modified": "2020-11-17 13:18:37.174448", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index c5f461abb6..5cb54d315f 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -44,7 +44,7 @@ "link_to": "POS Profile", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -53,7 +53,7 @@ "link_to": "POS Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -69,7 +69,7 @@ "link_to": "Loyalty Program", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -78,7 +78,7 @@ "link_to": "Loyalty Point Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -94,7 +94,7 @@ "link_to": "POS Opening Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -103,10 +103,10 @@ "link_to": "POS Closing Entry", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:29.114099", + "modified": "2020-11-17 13:18:39.383674", "modified_by": "Administrator", "module": "Selling", "name": "Retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 23edc259da..29bc549cca 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -60,7 +60,7 @@ "link_to": "Customer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -69,7 +69,7 @@ "link_to": "Quotation", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -78,7 +78,7 @@ "link_to": "Sales Order", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -87,7 +87,7 @@ "link_to": "Sales Invoice", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -96,7 +96,7 @@ "link_to": "Blanket Order", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -105,7 +105,7 @@ "link_to": "Sales Partner", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -114,7 +114,7 @@ "link_to": "Sales Person", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -130,7 +130,7 @@ "link_to": "Item", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -139,7 +139,7 @@ "link_to": "Item Price", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -148,7 +148,7 @@ "link_to": "Price List", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -157,7 +157,7 @@ "link_to": "Item Group", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -166,7 +166,7 @@ "link_to": "Product Bundle", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -175,7 +175,7 @@ "link_to": "Promotional Scheme", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -184,7 +184,7 @@ "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -193,7 +193,7 @@ "link_to": "Shipping Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -202,7 +202,7 @@ "link_to": "Coupon Code", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -218,7 +218,7 @@ "link_to": "Selling Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -227,7 +227,7 @@ "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -236,7 +236,7 @@ "link_to": "Sales Taxes and Charges Template", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -245,7 +245,7 @@ "link_to": "Lead Source", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -254,7 +254,7 @@ "link_to": "Customer Group", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -263,7 +263,7 @@ "link_to": "Contact", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -272,7 +272,7 @@ "link_to": "Address", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -281,7 +281,7 @@ "link_to": "Territory", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -290,7 +290,7 @@ "link_to": "Campaign", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -306,7 +306,7 @@ "link_to": "Sales Analytics", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -315,7 +315,7 @@ "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -324,7 +324,7 @@ "link_to": "sales-funnel", "link_type": "Page", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -333,7 +333,7 @@ "link_to": "Sales Order Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -342,7 +342,7 @@ "link_to": "Quotation Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -351,7 +351,7 @@ "link_to": "Customer Acquisition and Loyalty", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -360,7 +360,7 @@ "link_to": "Inactive Customers", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -369,7 +369,7 @@ "link_to": "Sales Person-wise Transaction Summary", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -378,7 +378,7 @@ "link_to": "Item-wise Sales History", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -394,7 +394,7 @@ "link_to": "Lead Details", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -403,7 +403,7 @@ "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -412,7 +412,7 @@ "link_to": "Available Stock for Packing Items", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -421,7 +421,7 @@ "link_to": "Pending SO Items For Purchase Request", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -430,7 +430,7 @@ "link_to": "Delivery Note Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -439,7 +439,7 @@ "link_to": "Sales Invoice Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -448,7 +448,7 @@ "link_to": "Customer Credit Balance", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -457,7 +457,7 @@ "link_to": "Customers Without Any Sales Transactions", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -466,7 +466,7 @@ "link_to": "Sales Partners Commission", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -475,7 +475,7 @@ "link_to": "Territory Target Variance Based On Item Group", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -484,7 +484,7 @@ "link_to": "Sales Person Target Variance Based On Item Group", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -493,10 +493,10 @@ "link_to": "Sales Partner Target Variance based on Item Group", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:30.100950", + "modified": "2020-11-17 13:18:38.357500", "modified_by": "Administrator", "module": "Selling", "name": "Selling", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index efa27e9a55..03a9fe4b65 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -15,7 +15,7 @@ "is_standard": 1, "label": "ERPNext Settings", "links": [], - "modified": "2020-11-17 13:00:26.889826", + "modified": "2020-11-17 13:18:41.371988", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/desk_page/home/home.json index 5f7ab0161e..6149226f01 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -74,7 +74,7 @@ "link_to": "Patient", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -83,7 +83,7 @@ "link_to": "Diagnosis", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -99,7 +99,7 @@ "link_to": "Crop", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -108,7 +108,7 @@ "link_to": "Crop Cycle", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -117,7 +117,7 @@ "link_to": "Location", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -126,7 +126,7 @@ "link_to": "Fertilizer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -142,7 +142,7 @@ "link_to": "Student", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -151,7 +151,7 @@ "link_to": "Course", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -160,7 +160,7 @@ "link_to": "Instructor", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -169,7 +169,7 @@ "link_to": "Room", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -185,7 +185,7 @@ "link_to": "Member", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -194,7 +194,7 @@ "link_to": "Volunteer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -203,7 +203,7 @@ "link_to": "Chapter", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -212,7 +212,7 @@ "link_to": "Donor", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -228,7 +228,7 @@ "link_to": "Warehouse", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -237,7 +237,7 @@ "link_to": "Brand", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -246,7 +246,7 @@ "link_to": "UOM", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -255,7 +255,7 @@ "link_to": "Stock Reconciliation", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -271,7 +271,7 @@ "link_to": "Employee", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -280,7 +280,7 @@ "link_to": "Employee Attendance Tool", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -289,7 +289,7 @@ "link_to": "Salary Structure", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -305,7 +305,7 @@ "link_to": "Lead", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -314,7 +314,7 @@ "link_to": "Customer Group", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -323,7 +323,7 @@ "link_to": "Territory", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -339,7 +339,7 @@ "link_to": "Item", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -348,7 +348,7 @@ "link_to": "Customer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -357,7 +357,7 @@ "link_to": "Supplier", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -366,7 +366,7 @@ "link_to": "Company", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -375,7 +375,7 @@ "link_to": "Account", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -384,7 +384,7 @@ "link_to": "Opening Invoice Creation Tool", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -400,7 +400,7 @@ "link_to": "Data Import", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -409,7 +409,7 @@ "link_to": "Chart of Accounts Importer", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -418,7 +418,7 @@ "link_to": "Letter Head", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -427,10 +427,10 @@ "link_to": "Email Account", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:26.284801", + "modified": "2020-11-17 13:18:41.967994", "modified_by": "Administrator", "module": "Setup", "name": "Home", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 86b3767f47..b545a42254 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -74,7 +74,7 @@ "link_to": "Item", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -83,7 +83,7 @@ "link_to": "Item Group", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -92,7 +92,7 @@ "link_to": "Product Bundle", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -101,7 +101,7 @@ "link_to": "Price List", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -110,7 +110,7 @@ "link_to": "Item Price", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -119,7 +119,7 @@ "link_to": "Shipping Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -128,7 +128,7 @@ "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -137,7 +137,7 @@ "link_to": "Item Alternative", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -146,7 +146,7 @@ "link_to": "Item Manufacturer", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -155,7 +155,7 @@ "link_to": "Customs Tariff Number", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -171,7 +171,7 @@ "link_to": "Material Request", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -180,7 +180,7 @@ "link_to": "Stock Entry", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -189,7 +189,7 @@ "link_to": "Delivery Note", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -198,7 +198,7 @@ "link_to": "Purchase Receipt", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -207,7 +207,7 @@ "link_to": "Pick List", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -216,7 +216,7 @@ "link_to": "Delivery Trip", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -232,7 +232,7 @@ "link_to": "Stock Ledger", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -241,7 +241,7 @@ "link_to": "Stock Balance", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -250,7 +250,7 @@ "link_to": "Stock Projected Qty", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -259,7 +259,7 @@ "link_to": "stock-balance", "link_type": "Page", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -268,7 +268,7 @@ "link_to": "Stock Ageing", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -277,7 +277,7 @@ "link_to": "Item Price Stock", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -293,7 +293,7 @@ "link_to": "Stock Settings", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -302,7 +302,7 @@ "link_to": "Warehouse", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -311,7 +311,7 @@ "link_to": "UOM", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -320,7 +320,7 @@ "link_to": "Item Variant Settings", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -329,7 +329,7 @@ "link_to": "Brand", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -338,7 +338,7 @@ "link_to": "Item Attribute", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -347,7 +347,7 @@ "link_to": "UOM Conversion Factor", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -363,7 +363,7 @@ "link_to": "Serial No", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -372,7 +372,7 @@ "link_to": "Batch", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -381,7 +381,7 @@ "link_to": "Installation Note", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -390,7 +390,7 @@ "link_to": "Serial No Service Contract Expiry", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -399,7 +399,7 @@ "link_to": "Serial No Status", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -408,7 +408,7 @@ "link_to": "Serial No Warranty Expiry", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -424,7 +424,7 @@ "link_to": "Stock Reconciliation", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -433,7 +433,7 @@ "link_to": "Landed Cost Voucher", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -442,7 +442,7 @@ "link_to": "Packing Slip", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -451,7 +451,7 @@ "link_to": "Quality Inspection", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -460,7 +460,7 @@ "link_to": "Quality Inspection Template", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -469,7 +469,7 @@ "link_to": "Quick Stock Balance", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -485,7 +485,7 @@ "link_to": "Item-wise Price List Rate", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -494,7 +494,7 @@ "link_to": "Stock Analytics", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -503,7 +503,7 @@ "link_to": "Stock Qty vs Serial No Count", "link_type": "Report", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -512,7 +512,7 @@ "link_to": "Delivery Note Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -521,7 +521,7 @@ "link_to": "Purchase Receipt Trends", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -530,7 +530,7 @@ "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -539,7 +539,7 @@ "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -548,7 +548,7 @@ "link_to": "Item Shortage Report", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -557,7 +557,7 @@ "link_to": "Batch-Wise Balance History", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -573,7 +573,7 @@ "link_to": "Requested Items To Be Transferred", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -582,7 +582,7 @@ "link_to": "Batch Item Expiry Status", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -591,7 +591,7 @@ "link_to": "Item Prices", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -600,7 +600,7 @@ "link_to": "Itemwise Recommended Reorder Level", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -609,7 +609,7 @@ "link_to": "Item Variant Details", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -618,7 +618,7 @@ "link_to": "Subcontracted Raw Materials To Be Transferred", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -627,7 +627,7 @@ "link_to": "Subcontracted Item To Be Received", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -636,10 +636,10 @@ "link_to": "Stock and Account Value Comparison", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:29.632812", + "modified": "2020-11-17 13:18:38.783880", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 4dd322a9ca..5a88307ef0 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -59,7 +59,7 @@ "link_to": "Issue", "link_type": "DocType", "onboard": 1, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -68,7 +68,7 @@ "link_to": "Issue Type", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -77,7 +77,7 @@ "link_to": "Issue Priority", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -93,7 +93,7 @@ "link_to": "Maintenance Schedule", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -102,7 +102,7 @@ "link_to": "Maintenance Visit", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -118,7 +118,7 @@ "link_to": "Service Level Agreement", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -134,7 +134,7 @@ "link_to": "Warranty Claim", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -143,7 +143,7 @@ "link_to": "Serial No", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -159,7 +159,7 @@ "link_to": "Support Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -175,10 +175,10 @@ "link_to": "First Response Time for Issues", "link_type": "Report", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:27.977688", + "modified": "2020-11-17 13:18:40.457842", "modified_by": "Administrator", "module": "Support", "name": "Support", diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json index 987b651506..3f654bea64 100644 --- a/erpnext/utilities/desk_page/utilities/utilities.json +++ b/erpnext/utilities/desk_page/utilities/utilities.json @@ -33,7 +33,7 @@ "link_to": "Video", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" }, { "hidden": 0, @@ -42,10 +42,10 @@ "link_to": "Video Settings", "link_type": "DocType", "onboard": 0, - "type": "Card Break" + "type": "Link" } ], - "modified": "2020-11-17 13:00:29.198857", + "modified": "2020-11-17 13:18:39.317897", "modified_by": "Administrator", "module": "Utilities", "name": "Utilities", From 19d5074c25646de3b0239eeadf56b50bb3eca667 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 15:49:00 +0530 Subject: [PATCH 190/283] fix: get formatted value in 'taxes' print template --- erpnext/templates/print_formats/includes/taxes.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/templates/print_formats/includes/taxes.html b/erpnext/templates/print_formats/includes/taxes.html index 6e984f3901..304e845287 100644 --- a/erpnext/templates/print_formats/includes/taxes.html +++ b/erpnext/templates/print_formats/includes/taxes.html @@ -20,10 +20,10 @@ {%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
-
+ +
- {{ frappe.format_value(frappe.utils.flt(charge.tax_amount), - table_meta.get_field("tax_amount"), doc, currency=doc.currency) }} + {{ charge.get_formatted('tax_amount', doc) }}
{%- endif -%} From 02d1491e39951a25b1c502fb9abf337e08a34b2c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 17:41:38 +0530 Subject: [PATCH 191/283] fix: order summary qty alignment --- erpnext/public/scss/point-of-sale.scss | 1 + .../page/point_of_sale/pos_past_order_summary.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index f388093548..f9757384cb 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -1067,6 +1067,7 @@ display: flex; text-align: right; margin-left: var(--margin-md); + justify-content: flex-end; > .item-disc { color: var(--dark-green-500); diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 28076db63b..4b75c0bb73 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -333,9 +333,25 @@ erpnext.PointOfSale.PastOrderSummary = class { doc.items.forEach((item, i) => { const item_dom = this.get_item_html(doc, item); this.$items_container.append(item_dom); + this.set_dynamic_rate_header_width(); }); } + set_dynamic_rate_header_width() { + const rate_cols = Array.from(this.$items_container.find(".item-rate-disc")); + this.$items_container.find(".item-rate-disc").css("width", ""); + let max_width = rate_cols.reduce((max_width, elm) => { + if ($(elm).width() > max_width) + max_width = $(elm).width(); + return max_width; + }, 0); + + max_width += 1; + if (max_width == 1) max_width = ""; + + this.$items_container.find(".item-rate-disc").css("width", max_width); + } + attach_payments_info(doc) { this.$payment_container.html(''); doc.payments.forEach(p => { From 66e8a12d0f010b9ec646964c56d59299d6fc842f Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 30 Nov 2020 17:48:13 +0530 Subject: [PATCH 192/283] fix: KeyError 'sourced_by_supplier' --- erpnext/manufacturing/doctype/bom/bom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 8888a96768..c6699200dc 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -169,8 +169,8 @@ class BOM(WebsiteGenerator): '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' : flt(rate) * (flt(self.conversion_rate) or 1), - 'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0, - 'sourced_by_supplier' : args['sourced_by_supplier'] or 0 + 'include_item_in_manufacturing': cint(args['transfer_for_manufacture'], 0), + 'sourced_by_supplier' : args.get('sourced_by_supplier', 0) } return ret_item From 188e0ecbcb11f994f16a1a1ff9a7fc5abb3bad7d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 17:53:19 +0530 Subject: [PATCH 193/283] fix: submit order summary center alignment --- .../page/point_of_sale/pos_past_order_summary.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 4b75c0bb73..eb29976371 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -299,12 +299,12 @@ erpnext.PointOfSale.PastOrderSummary = class { } load_summary_of(doc, after_submission=false) { - this.toggle_summary_placeholder(false) - after_submission ? - this.$summary_wrapper.css('grid-column', 'span 10 / span 10') : - this.$summary_wrapper.css('grid-column', 'span 6 / span 6') - + this.$component.css('grid-column', 'span 10 / span 10') : + this.$component.css('grid-column', 'span 6 / span 6') + + this.toggle_summary_placeholder(false) + this.doc = doc; this.attach_document_info(doc); From ccf5dc66e2162164210302f5a67bd67c8142a0af Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 1 Dec 2020 09:11:05 +0530 Subject: [PATCH 194/283] feat: multi-currency payroll (#23519) * feat: multi-currency payroll * fix: refactor and added conditions * fix: uncommented code * style: removed comments * fix: missing argument * style: styling changes * fix: test cases * Update asset_value_adjustment.py * patch: update columns * style: formating * style: formatting * fix: 1st review * fix: refactor * Revert "fix: refactor" This reverts commit eca0e17d11a192d60f249b2af992971c625aec46. reverting to previous state * Revert "fix: 1st review" This reverts commit 7eac48b102157df4353598f73b2ea97308af436a. reverting before 1st review * fix: 2nd review changes * fix: test cases * fix: added call to fetch exchange rate * fix: remove unnecessary code * fix: refactor * fix: refactor patch * fix: refactor * fix: refactor * fix: clear test data * fix: slider * feat: multi-currency payroll * fix: refactor and added conditions * fix: uncommented code * style: removed comments * fix: missing argument * style: styling changes * fix: test cases * patch: update columns * Update asset_value_adjustment.py * style: formating * style: formatting * fix: 1st review * fix: refactor * Revert "fix: refactor" This reverts commit eca0e17d11a192d60f249b2af992971c625aec46. reverting to previous state * Revert "fix: 1st review" This reverts commit 7eac48b102157df4353598f73b2ea97308af436a. reverting before 1st review * fix: 2nd review changes * fix: test cases * fix: added call to fetch exchange rate * fix: remove unnecessary code * fix: refactor * fix: refactor patch * fix: refactor * fix: refactor * fix: clear test data * fix: slider * feat: Added company field in leave encashment and employee benefit * refactor: Refactored multi-currency payroll patch * fix: currency column in salary register * refactor: Refactored code for making bank and return entry against employee advance * fix: minor cleanup * fix: fixed translation * fix: removed salary component type * fix: fixed sider issues * fix: translation and slider * style: formatted msg * fix: fixed slider * fix: travis * fix: refactor * fix: slider * fix: slider * fix: slider * fix: travis * fix: patch * fix: patch * fix: travis * fix: travis * fix: travis * fix: travis * fix: travis * fix: travis * fix: re-run travis * fix: rerun travis * fix: rerun travis * fix: rerun travis * fix: travis rerun * fix: increased throttle_user_limit from 60 to 100 * fix: patch * fix: patch * fix: assign payroll payable account as default payroll payable account in SSA * fix: removed debugger * fix: slider Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Co-authored-by: Nabin Hait --- .travis/site_config.json | 3 +- .../mode_of_payment/mode_of_payment.js | 24 +- .../doctype/payment_entry/payment_entry.py | 350 ++++++++++++------ .../salary_component_account.json | 118 ++---- erpnext/demo/setup/setup_data.py | 2 +- .../employee_advance/employee_advance.js | 89 ++++- .../employee_advance/employee_advance.json | 32 +- .../employee_advance/employee_advance.py | 104 ++++-- .../employee_advance/test_employee_advance.py | 12 +- .../expense_claim/test_expense_claim.py | 4 + .../expense_taxes_and_charges.json | 10 +- .../leave_encashment/leave_encashment.js | 24 +- .../leave_encashment/leave_encashment.json | 33 +- .../leave_encashment/leave_encashment.py | 7 + .../leave_encashment/test_leave_encashment.py | 10 +- .../loan_management/doctype/loan/loan.json | 2 +- erpnext/loan_management/doctype/loan/loan.py | 15 +- .../loan_management/doctype/loan/test_loan.py | 2 + .../loan_application/test_loan_application.py | 4 +- erpnext/patches.txt | 2 + .../create_salary_structure_assignments.py | 13 +- .../updates_for_multi_currency_payroll.py | 136 +++++++ .../additional_salary/additional_salary.js | 52 +++ .../additional_salary/additional_salary.json | 29 +- .../additional_salary/additional_salary.py | 5 + .../test_additional_salary.py | 15 +- .../employee_benefit_application.js | 50 ++- .../employee_benefit_application.json | 26 +- .../employee_benefit_application.py | 29 +- .../employee_benefit_application_detail.json | 4 +- .../employee_benefit_claim.js | 19 + .../employee_benefit_claim.json | 24 +- .../employee_incentive/employee_incentive.js | 53 ++- .../employee_incentive.json | 27 +- .../employee_incentive/employee_incentive.py | 9 + .../employee_tax_exemption_declaration.json | 14 +- ...test_employee_tax_exemption_declaration.py | 4 + ...ee_tax_exemption_declaration_category.json | 4 +- ...employee_tax_exemption_proof_submission.js | 4 + ...ployee_tax_exemption_proof_submission.json | 14 +- ...tax_exemption_proof_submission_detail.json | 6 +- .../income_tax_slab/income_tax_slab.js | 4 +- .../income_tax_slab/income_tax_slab.json | 16 +- .../income_tax_slab_other_charges.json | 6 +- .../payroll_employee_detail.json | 2 +- .../doctype/payroll_entry/payroll_entry.js | 40 ++ .../doctype/payroll_entry/payroll_entry.json | 32 +- .../doctype/payroll_entry/payroll_entry.py | 137 +++++-- .../payroll_entry/test_payroll_entry.py | 88 ++++- .../test_set_salary_components.js | 16 +- .../retention_bonus/retention_bonus.js | 17 + .../retention_bonus/retention_bonus.json | 18 +- .../salary_component/salary_component.js | 2 +- .../doctype/salary_detail/salary_detail.json | 9 +- .../doctype/salary_slip/salary_slip.js | 190 ++++++++-- .../doctype/salary_slip/salary_slip.json | 136 +++++-- .../doctype/salary_slip/salary_slip.py | 65 +++- .../doctype/salary_slip/test_salary_slip.py | 107 ++++-- .../salary_structure/salary_structure.js | 57 ++- .../salary_structure/salary_structure.json | 126 ++----- .../salary_structure/salary_structure.py | 57 ++- .../salary_structure/test_salary_structure.py | 26 +- .../salary_structure_assignment.js | 27 +- .../salary_structure_assignment.json | 28 +- .../salary_structure_assignment.py | 27 ++ .../taxable_salary_slab.json | 6 +- .../report/salary_register/salary_register.js | 24 +- .../report/salary_register/salary_register.py | 79 ++-- 68 files changed, 2043 insertions(+), 683 deletions(-) create mode 100644 erpnext/patches/v13_0/updates_for_multi_currency_payroll.py diff --git a/.travis/site_config.json b/.travis/site_config.json index dae80095d4..572bbd0853 100644 --- a/.travis/site_config.json +++ b/.travis/site_config.json @@ -9,5 +9,6 @@ "root_login": "root", "root_password": "travis", "host_name": "http://test_site:8000", - "install_apps": ["erpnext"] + "install_apps": ["erpnext"], + "throttle_user_limit": 100 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js index d3040c8db8..7a06d3572a 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js @@ -1,13 +1,17 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -cur_frm.set_query("default_account", "accounts", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return{ - filters: [ - ['Account', 'account_type', 'in', 'Bank, Cash, Receivable'], - ['Account', 'is_group', '=', 0], - ['Account', 'company', '=', d.company] - ] - } -}); +frappe.ui.form.on('Mode of Payment', { + setup: function(frm) { + frm.set_query("default_account", "accounts", function(doc, cdt, cdn) { + let d = locals[cdt][cdn]; + return { + filters: [ + ['Account', 'account_type', 'in', 'Bank, Cash, Receivable'], + ['Account', 'is_group', '=', 0], + ['Account', 'company', '=', d.company] + ] + }; + }); + }, +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 11ab02021b..31a4c8a387 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -202,17 +202,32 @@ class PaymentEntry(AccountsController): # if account_type not in account_types: # frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) - def set_exchange_rate(self): + def set_exchange_rate(self, ref_doc=None): + self.set_source_exchange_rate(ref_doc) + self.set_target_exchange_rate(ref_doc) + + def set_source_exchange_rate(self, ref_doc=None): if self.paid_from and not self.source_exchange_rate: if self.paid_from_account_currency == self.company_currency: self.source_exchange_rate = 1 else: - self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, - self.company_currency, self.posting_date) + if ref_doc: + if self.paid_from_account_currency == ref_doc.currency: + self.source_exchange_rate = ref_doc.get("exchange_rate") + if not self.source_exchange_rate: + self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, + self.company_currency, self.posting_date) + + def set_target_exchange_rate(self, ref_doc=None): if self.paid_to and not self.target_exchange_rate: - self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, - self.company_currency, self.posting_date) + if ref_doc: + if self.paid_to_account_currency == ref_doc.currency: + self.target_exchange_rate = ref_doc.get("exchange_rate") + + if not self.target_exchange_rate: + self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, + self.company_currency, self.posting_date) def validate_mandatory(self): for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"): @@ -282,9 +297,10 @@ class PaymentEntry(AccountsController): 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.") - .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")), + frappe.msgprint( + _("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.") + .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")) + + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), title=_("Warning"), indicator="orange") @@ -909,22 +925,24 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre exchange_rate = 1 outstanding_amount = get_outstanding_on_journal_entry(reference_name) elif reference_doctype != "Journal Entry": - if party_account_currency == company_currency: - if ref_doc.doctype == "Expense Claim": + if ref_doc.doctype == "Expense Claim": total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) - elif ref_doc.doctype == "Employee Advance": - total_amount = ref_doc.advance_amount - else: + elif ref_doc.doctype == "Employee Advance": + total_amount = ref_doc.advance_amount + exchange_rate = ref_doc.get("exchange_rate") + if party_account_currency != ref_doc.currency: + total_amount = flt(total_amount) * flt(exchange_rate) + if not total_amount: + if party_account_currency == company_currency: total_amount = ref_doc.base_grand_total - exchange_rate = 1 - else: - total_amount = ref_doc.grand_total - + exchange_rate = 1 + else: + total_amount = ref_doc.grand_total + if not exchange_rate: # Get the exchange rate from the original ref doc - # or get it based on the posting date of the ref doc + # or get it based on the posting date of the ref doc. exchange_rate = ref_doc.get("conversion_rate") or \ get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) - if reference_doctype in ("Sales Invoice", "Purchase Invoice"): outstanding_amount = ref_doc.get("outstanding_amount") bill_no = ref_doc.get("bill_no") @@ -932,11 +950,15 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\ - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount")) elif reference_doctype == "Employee Advance": - outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount) + outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)) + if party_account_currency != ref_doc.currency: + outstanding_amount = flt(outstanding_amount) * flt(exchange_rate) + if party_account_currency == company_currency: + exchange_rate = 1 else: outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) else: - # Get the exchange rate based on the posting date of the ref doc + # Get the exchange rate based on the posting date of the ref doc. exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) @@ -948,102 +970,104 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre "bill_no": bill_no }) +def get_amounts_based_on_reference_doctype(reference_doctype, ref_doc, party_account_currency, company_currency, reference_name): + total_amount, outstanding_amount, exchange_rate = None + if reference_doctype == "Fees": + total_amount = ref_doc.get("grand_total") + exchange_rate = 1 + outstanding_amount = ref_doc.get("outstanding_amount") + elif reference_doctype == "Dunning": + total_amount = ref_doc.get("dunning_amount") + exchange_rate = 1 + outstanding_amount = ref_doc.get("dunning_amount") + elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: + total_amount = ref_doc.get("total_amount") + if ref_doc.multi_currency: + exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) + else: + exchange_rate = 1 + outstanding_amount = get_outstanding_on_journal_entry(reference_name) + + return total_amount, outstanding_amount, exchange_rate + +def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_currency, company_currency): + total_amount, outstanding_amount, exchange_rate = None + if ref_doc.doctype == "Expense Claim": + total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) + elif ref_doc.doctype == "Employee Advance": + total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc) + + if not total_amount: + total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency( + party_account_currency, company_currency, ref_doc) + + if not exchange_rate: + # Get the exchange rate from the original ref doc + # or get it based on the posting date of the ref doc + exchange_rate = ref_doc.get("conversion_rate") or \ + get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) + + outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts( + reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency) + + return total_amount, outstanding_amount, exchange_rate, bill_no + +def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc): + total_amount = ref_doc.advance_amount + exchange_rate = ref_doc.get("exchange_rate") + if party_account_currency != ref_doc.currency: + total_amount = flt(total_amount) * flt(exchange_rate) + + return total_amount, exchange_rate + +def get_total_amount_exchange_rate_base_on_currency(party_account_currency, company_currency, ref_doc): + exchange_rate = None + if party_account_currency == company_currency: + total_amount = ref_doc.base_grand_total + exchange_rate = 1 + else: + total_amount = ref_doc.grand_total + + return total_amount, exchange_rate + +def get_bill_no_and_update_amounts(reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency): + outstanding_amount, bill_no = None + if reference_doctype in ("Sales Invoice", "Purchase Invoice"): + outstanding_amount = ref_doc.get("outstanding_amount") + bill_no = ref_doc.get("bill_no") + elif reference_doctype == "Expense Claim": + outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\ + - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount")) + elif reference_doctype == "Employee Advance": + outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)) + if party_account_currency != ref_doc.currency: + outstanding_amount = flt(outstanding_amount) * flt(exchange_rate) + if party_account_currency == company_currency: + exchange_rate = 1 + else: + outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) + + return outstanding_amount, exchange_rate, bill_no + @frappe.whitelist() def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): + reference_doc = None doc = frappe.get_doc(dt, dn) if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) - if dt in ("Sales Invoice", "Sales Order", "Dunning"): - party_type = "Customer" - elif dt in ("Purchase Invoice", "Purchase Order"): - party_type = "Supplier" - elif dt in ("Expense Claim", "Employee Advance"): - party_type = "Employee" - elif dt in ("Fees"): - party_type = "Student" - - # party account - if dt == "Sales Invoice": - party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to - elif dt == "Purchase Invoice": - party_account = doc.credit_to - elif dt == "Fees": - party_account = doc.receivable_account - elif dt == "Employee Advance": - party_account = doc.advance_account - elif dt == "Expense Claim": - party_account = doc.payable_account - else: - party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) - - if dt not in ("Sales Invoice", "Purchase Invoice"): - party_account_currency = get_account_currency(party_account) - else: - party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) - - # payment type - if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \ - or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): - payment_type = "Receive" - else: - payment_type = "Pay" - - # amounts - grand_total = outstanding_amount = 0 - if party_amount: - grand_total = outstanding_amount = party_amount - elif dt in ("Sales Invoice", "Purchase Invoice"): - if party_account_currency == doc.company_currency: - grand_total = doc.base_rounded_total or doc.base_grand_total - else: - grand_total = doc.rounded_total or doc.grand_total - outstanding_amount = doc.outstanding_amount - elif dt in ("Expense Claim"): - grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges - outstanding_amount = doc.grand_total \ - - doc.total_amount_reimbursed - elif dt == "Employee Advance": - grand_total = doc.advance_amount - outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) - elif dt == "Fees": - grand_total = doc.grand_total - outstanding_amount = doc.outstanding_amount - elif dt == "Dunning": - grand_total = doc.grand_total - outstanding_amount = doc.grand_total - else: - if party_account_currency == doc.company_currency: - grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) - else: - grand_total = flt(doc.get("rounded_total") or doc.grand_total) - outstanding_amount = grand_total - flt(doc.advance_paid) + party_type = set_party_type(dt) + party_account = set_party_account(dt, dn, doc, party_type) + party_account_currency = set_party_account_currency(dt, party_account, doc) + payment_type = set_payment_type(dt, doc) + grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc) # bank or cash - bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), - account=bank_account) + bank = get_bank_cash_account(doc, bank_account) - if not bank: - bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), - account=bank_account) - - paid_amount = received_amount = 0 - if party_account_currency == bank.account_currency: - paid_amount = received_amount = abs(outstanding_amount) - elif payment_type == "Receive": - paid_amount = abs(outstanding_amount) - if bank_amount: - received_amount = bank_amount - else: - received_amount = paid_amount * doc.get('conversion_rate', 1) - else: - received_amount = abs(outstanding_amount) - if bank_amount: - paid_amount = bank_amount - else: - # if party account currency and bank currency is different then populate paid amount as well - paid_amount = received_amount * doc.get('conversion_rate', 1) + paid_amount, received_amount = set_paid_amount_and_received_amount( + dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc) pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type @@ -1115,10 +1139,120 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.setup_party_account_field() pe.set_missing_values() if party_account and bank: - pe.set_exchange_rate() + if dt == "Employee Advance": + reference_doc = doc + pe.set_exchange_rate(ref_doc=reference_doc) pe.set_amounts() return pe +def get_bank_cash_account(doc, bank_account): + bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), + account=bank_account) + + if not bank: + bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), + account=bank_account) + + return bank + +def set_party_type(dt): + if dt in ("Sales Invoice", "Sales Order", "Dunning"): + party_type = "Customer" + elif dt in ("Purchase Invoice", "Purchase Order"): + party_type = "Supplier" + elif dt in ("Expense Claim", "Employee Advance"): + party_type = "Employee" + elif dt in ("Fees"): + party_type = "Student" + return party_type + +def set_party_account(dt, dn, doc, party_type): + if dt == "Sales Invoice": + party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to + elif dt == "Purchase Invoice": + party_account = doc.credit_to + elif dt == "Fees": + party_account = doc.receivable_account + elif dt == "Employee Advance": + party_account = doc.advance_account + elif dt == "Expense Claim": + party_account = doc.payable_account + else: + party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) + return party_account + +def set_party_account_currency(dt, party_account, doc): + if dt not in ("Sales Invoice", "Purchase Invoice"): + party_account_currency = get_account_currency(party_account) + else: + party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) + return party_account_currency + +def set_payment_type(dt, doc): + if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \ + or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): + payment_type = "Receive" + else: + payment_type = "Pay" + return payment_type + +def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc): + grand_total = outstanding_amount = 0 + if party_amount: + grand_total = outstanding_amount = party_amount + elif dt in ("Sales Invoice", "Purchase Invoice"): + if party_account_currency == doc.company_currency: + grand_total = doc.base_rounded_total or doc.base_grand_total + else: + grand_total = doc.rounded_total or doc.grand_total + outstanding_amount = doc.outstanding_amount + elif dt in ("Expense Claim"): + grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges + outstanding_amount = doc.grand_total \ + - doc.total_amount_reimbursed + elif dt == "Employee Advance": + grand_total = flt(doc.advance_amount) + outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) + if party_account_currency != doc.currency: + grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate) + outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate) + elif dt == "Fees": + grand_total = doc.grand_total + outstanding_amount = doc.outstanding_amount + elif dt == "Dunning": + grand_total = doc.grand_total + outstanding_amount = doc.grand_total + else: + if party_account_currency == doc.company_currency: + grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) + else: + grand_total = flt(doc.get("rounded_total") or doc.grand_total) + outstanding_amount = grand_total - flt(doc.advance_paid) + return grand_total, outstanding_amount + +def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc): + paid_amount = received_amount = 0 + if party_account_currency == bank.account_currency: + paid_amount = received_amount = abs(outstanding_amount) + elif payment_type == "Receive": + paid_amount = abs(outstanding_amount) + if bank_amount: + received_amount = bank_amount + else: + received_amount = paid_amount * doc.get('conversion_rate', 1) + if dt == "Employee Advance": + received_amount = paid_amount * doc.get('exchange_rate', 1) + else: + received_amount = abs(outstanding_amount) + if bank_amount: + paid_amount = bank_amount + else: + # if party account currency and bank currency is different then populate paid amount as well + paid_amount = received_amount * doc.get('conversion_rate', 1) + if dt == "Employee Advance": + paid_amount = received_amount * doc.get('exchange_rate', 1) + return paid_amount, received_amount + def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: diff --git a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json index 23dc6c47e8..f1ed8efa31 100644 --- a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json +++ b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json @@ -1,92 +1,38 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-07-27 17:24:24.956896", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-07-27 17:24:24.956896", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "account" + ], "fields": [ { - "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_list_view": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.", - "fieldname": "default_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Default Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.", + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account" } - ], - "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": "2016-09-02 07:49:06.567389", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Salary Component Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-10-18 17:57:57.110257", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Salary Component Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py index a395c7c17a..05ee28a24a 100644 --- a/erpnext/demo/setup/setup_data.py +++ b/erpnext/demo/setup/setup_data.py @@ -134,7 +134,7 @@ def setup_employee(): salary_component = frappe.get_doc('Salary Component', d.name) salary_component.append('accounts', dict( company=erpnext.get_default_company(), - default_account=frappe.get_value('Account', dict(account_name=('like', 'Salary%'))) + account=frappe.get_value('Account', dict(account_name=('like', 'Salary%'))) )) salary_component.save() diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index cba8ee9a40..7056adf208 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -15,11 +15,16 @@ frappe.ui.form.on('Employee Advance', { }); frm.set_query("advance_account", function() { + if (!frm.doc.employee) { + frappe.msgprint(__("Please select employee first")); + } + var company_currency = erpnext.get_currency(frm.doc.company); return { filters: { "root_type": "Asset", "is_group": 0, - "company": frm.doc.company + "company": frm.doc.company, + "account_currency": ["in", [frm.doc.currency, company_currency]], } }; }); @@ -63,7 +68,7 @@ frappe.ui.form.on('Employee Advance', { }, __('Create')); }else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){ frm.add_custom_button(__("Deduction from salary"), function() { - frm.events.make_deduction_via_additional_salary(frm) + frm.events.make_deduction_via_additional_salary(frm); }, __('Create')); } } @@ -127,7 +132,9 @@ frappe.ui.form.on('Employee Advance', { 'employee_advance_name': frm.doc.name, 'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount), 'advance_account': frm.doc.advance_account, - 'mode_of_payment': frm.doc.mode_of_payment + 'mode_of_payment': frm.doc.mode_of_payment, + 'currency': frm.doc.currency, + 'exchange_rate': frm.doc.exchange_rate }, callback: function(r) { const doclist = frappe.model.sync(r.message); @@ -138,16 +145,72 @@ 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_pending_amount", - args: { - "employee": frm.doc.employee, - "posting_date": frm.doc.posting_date - }, - callback: function(r) { - frm.set_value("pending_amount",r.message); - } - }); + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('get_pending_amount') + ]); } + }, + + get_pending_amount: function(frm) { + frappe.call({ + 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("pending_amount", r.message); + } + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, + + currency: function(frm) { + var from_currency = frm.doc.currency; + var company_currency; + if (!frm.doc.company) { + company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } else { + company_currency = erpnext.get_currency(frm.doc.company); + } + if (from_currency != company_currency) { + frm.events.set_exchange_rate(frm, from_currency, company_currency); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + } + frm.refresh_fields(); + }, + + set_exchange_rate: function(frm, from_currency, company_currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: from_currency, + to_currency: company_currency, + }, + callback: function(r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + }); } }); diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 0d90913871..cf6b5404ec 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -13,6 +13,8 @@ "department", "column_break_4", "posting_date", + "currency", + "exchange_rate", "repay_unclaimed_amount_from_salary", "section_break_8", "purpose", @@ -91,7 +93,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Advance Amount", - "options": "Company:company:default_currency", + "options": "currency", "reqd": 1 }, { @@ -99,7 +101,7 @@ "fieldtype": "Currency", "label": "Paid Amount", "no_copy": 1, - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -107,7 +109,7 @@ "fieldtype": "Currency", "label": "Claimed Amount", "no_copy": 1, - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -161,7 +163,7 @@ "fieldname": "return_amount", "fieldtype": "Currency", "label": "Returned Amount", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -175,13 +177,31 @@ "fieldname": "pending_amount", "fieldtype": "Currency", "label": "Pending Amount", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "depends_on": "currency", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "print_hide": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-12 12:42:39.833818", + "modified": "2020-11-25 12:01:55.980721", "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 3c435b8cc3..cb72f6b6d9 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -19,7 +19,6 @@ class EmployeeAdvance(Document): def validate(self): self.set_status() - self.validate_employee_advance_account() def on_cancel(self): self.ignore_linked_doctypes = ('GL Entry') @@ -38,16 +37,9 @@ class EmployeeAdvance(Document): elif self.docstatus == 2: self.status = "Cancelled" - def validate_employee_advance_account(self): - company_currency = erpnext.get_company_currency(self.company) - if (self.advance_account and - company_currency != frappe.db.get_value('Account', self.advance_account, 'account_currency')): - frappe.throw(_("Advance account currency should be same as company currency {0}") - .format(company_currency)) - def set_total_advance_paid(self): paid_amount = frappe.db.sql(""" - select ifnull(sum(debit_in_account_currency), 0) as paid_amount + select ifnull(sum(debit), 0) as paid_amount from `tabGL Entry` where against_voucher_type = 'Employee Advance' and against_voucher = %s @@ -56,7 +48,7 @@ class EmployeeAdvance(Document): """, (self.name, self.employee), as_dict=1)[0].paid_amount return_amount = frappe.db.sql(""" - select name, ifnull(sum(credit_in_account_currency), 0) as return_amount + select ifnull(sum(credit), 0) as return_amount from `tabGL Entry` where against_voucher_type = 'Employee Advance' and voucher_type != 'Expense Claim' @@ -65,6 +57,11 @@ class EmployeeAdvance(Document): and party = %s """, (self.name, self.employee), as_dict=1)[0].return_amount + if paid_amount != 0: + paid_amount = flt(paid_amount) / flt(self.exchange_rate) + if return_amount != 0: + return_amount = flt(return_amount) / flt(self.exchange_rate) + if flt(paid_amount) > self.advance_amount: frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"), EmployeeAdvanceOverPayment) @@ -107,16 +104,27 @@ def make_bank_entry(dt, dn): doc = frappe.get_doc(dt, dn) payment_account = get_default_bank_cash_account(doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment) + if not payment_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + + advance_account_currency = frappe.db.get_value('Account', doc.advance_account, 'account_currency') + + advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate(advance_account_currency,doc ) + + paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc) je = frappe.new_doc("Journal Entry") je.posting_date = nowdate() je.voucher_type = 'Bank Entry' je.company = doc.company je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose + je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0 je.append("accounts", { "account": doc.advance_account, - "debit_in_account_currency": flt(doc.advance_amount), + "account_currency": advance_account_currency, + "exchange_rate": flt(advance_exchange_rate), + "debit_in_account_currency": flt(advance_amount), "reference_type": "Employee Advance", "reference_name": doc.name, "party_type": "Employee", @@ -128,19 +136,41 @@ def make_bank_entry(dt, dn): je.append("accounts", { "account": payment_account.account, "cost_center": erpnext.get_default_cost_center(doc.company), - "credit_in_account_currency": flt(doc.advance_amount), + "credit_in_account_currency": flt(paying_amount), "account_currency": payment_account.account_currency, - "account_type": payment_account.account_type + "account_type": payment_account.account_type, + "exchange_rate": flt(paying_exchange_rate) }) return je.as_dict() +def get_advance_amount_advance_exchange_rate(advance_account_currency, doc): + if advance_account_currency != doc.currency: + advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) + advance_exchange_rate = 1 + else: + advance_amount = doc.advance_amount + advance_exchange_rate = doc.exchange_rate + + return advance_amount, advance_exchange_rate + +def get_paying_amount_paying_exchange_rate(payment_account, doc): + if payment_account.account_currency != doc.currency: + paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate) + paying_exchange_rate = 1 + else: + paying_amount = doc.advance_amount + paying_exchange_rate = doc.exchange_rate + + return paying_amount, paying_exchange_rate + @frappe.whitelist() def create_return_through_additional_salary(doc): import json doc = frappe._dict(json.loads(doc)) additional_salary = frappe.new_doc('Additional Salary') additional_salary.employee = doc.employee + additional_salary.currency = doc.currency additional_salary.amount = doc.paid_amount - doc.claimed_amount additional_salary.company = doc.company additional_salary.ref_doctype = doc.doctype @@ -149,26 +179,28 @@ def create_return_through_additional_salary(doc): return additional_salary @frappe.whitelist() -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 = '' - if mode_of_payment: - mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type') - if mode_of_payment_type not in ["Cash", "Bank"]: - # if mode of payment is General then it unset the type - mode_of_payment_type = None - +def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, currency, exchange_rate, mode_of_payment=None): + bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) + if not bank_cash_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + + advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency') + je = frappe.new_doc('Journal Entry') je.posting_date = nowdate() - # if mode of payment is Bank then voucher type is Bank Entry - je.voucher_type = '{} Entry'.format(mode_of_payment_type) if mode_of_payment_type else 'Cash Entry' + je.voucher_type = get_voucher_type(mode_of_payment) je.company = company je.remark = 'Return against Employee Advance: ' + employee_advance_name + je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0 + + advance_account_amount = flt(return_amount) if advance_account_currency==currency \ + else flt(return_amount) * flt(exchange_rate) je.append('accounts', { 'account': advance_account, - 'credit_in_account_currency': return_amount, + 'credit_in_account_currency': advance_account_amount, + 'account_currency': advance_account_currency, + 'exchange_rate': flt(exchange_rate) if advance_account_currency == currency else 1, 'reference_type': 'Employee Advance', 'reference_name': employee_advance_name, 'party_type': 'Employee', @@ -176,13 +208,25 @@ def make_return_entry(employee, company, employee_advance_name, return_amount, 'is_advance': 'Yes' }) + bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \ + else flt(return_amount) * flt(exchange_rate) + je.append("accounts", { - "account": return_account.account, - "debit_in_account_currency": return_amount, - "account_currency": return_account.account_currency, - "account_type": return_account.account_type + "account": bank_cash_account.account, + "debit_in_account_currency": bank_amount, + "account_currency": bank_cash_account.account_currency, + "account_type": bank_cash_account.account_type, + "exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1 }) return je.as_dict() +def get_voucher_type(mode_of_payment=None): + voucher_type = "Cash Entry" + if mode_of_payment: + mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type') + if mode_of_payment_type == "Bank": + voucher_type = "Bank Entry" + + return voucher_type \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py index 2097e711de..c88b2b8e49 100644 --- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py @@ -3,15 +3,17 @@ # See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext import unittest from frappe.utils import nowdate from erpnext.hr.doctype.employee_advance.employee_advance import make_bank_entry from erpnext.hr.doctype.employee_advance.employee_advance import EmployeeAdvanceOverPayment +from erpnext.hr.doctype.employee.test_employee import make_employee class TestEmployeeAdvance(unittest.TestCase): def test_paid_amount_and_status(self): - advance = make_employee_advance() + employee_name = make_employee("_T@employe.advance") + advance = make_employee_advance(employee_name) journal_entry = make_payment_entry(advance) journal_entry.submit() @@ -33,11 +35,13 @@ def make_payment_entry(advance): return journal_entry -def make_employee_advance(): +def make_employee_advance(employee_name): doc = frappe.new_doc("Employee Advance") - doc.employee = "_T-Employee-00001" + doc.employee = employee_name doc.company = "_Test company" doc.purpose = "For site visit" + doc.currency = erpnext.get_company_currency("_Test company") + doc.exchange_rate = 1 doc.advance_amount = 1000 doc.posting_date = nowdate() doc.advance_account = "_Test Employee Advance - _TC" diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 6e97f0513d..4a0908d457 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -7,6 +7,7 @@ import unittest from frappe.utils import random_string, nowdate from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry from erpnext.accounts.doctype.account.test_account import create_account +from erpnext.hr.doctype.employee.test_employee import make_employee test_records = frappe.get_test_records('Expense Claim') test_dependencies = ['Employee'] @@ -126,6 +127,9 @@ def generate_taxes(): def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) + if not employee: + employee = make_employee("test_employee@expense_claim.com", company=company) + currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center']) expense_claim = { "doctype": "Expense Claim", 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 885e3eed97..020457d4ec 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 @@ -71,9 +71,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "oldfieldname": "tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency" + "options": "currency" }, { "columns": 2, @@ -81,9 +79,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Total", - "oldfieldname": "total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -106,7 +102,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-11 19:01:26.611758", + "modified": "2020-09-23 20:27:36.027728", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.js b/erpnext/hr/doctype/leave_encashment/leave_encashment.js index 71a34226da..81936a4a38 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.js +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.js @@ -22,7 +22,12 @@ frappe.ui.form.on('Leave Encashment', { } }, employee: function(frm) { - frm.trigger("get_leave_details_for_encashment"); + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('get_leave_details_for_encashment') + ]); + } }, leave_type: function(frm) { frm.trigger("get_leave_details_for_encashment"); @@ -40,5 +45,20 @@ frappe.ui.form.on('Leave Encashment', { } }); } - } + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, }); diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.json b/erpnext/hr/doctype/leave_encashment/leave_encashment.json index 2cf6ccf5ca..83eeae3adb 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.json +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.json @@ -12,6 +12,7 @@ "employee", "employee_name", "department", + "company", "column_break_4", "leave_type", "leave_allocation", @@ -19,9 +20,11 @@ "encashable_days", "amended_from", "payroll", - "encashment_amount", "encashment_date", - "additional_salary" + "additional_salary", + "column_break_14", + "currency", + "encashment_amount" ], "fields": [ { @@ -109,6 +112,7 @@ "in_list_view": 1, "label": "Encashment Amount", "no_copy": 1, + "options": "currency", "read_only": 1 }, { @@ -124,11 +128,34 @@ "no_copy": 1, "options": "Additional Salary", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2019-12-16 11:51:57.732223", + "modified": "2020-11-25 11:56:06.777241", "modified_by": "Administrator", "module": "HR", "name": "Leave Encashment", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index c1dcc97b1a..4c1a46522f 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -16,10 +16,16 @@ class LeaveEncashment(Document): def validate(self): set_employee_name(self) self.get_leave_details_for_encashment() + self.validate_salary_structure() if not self.encashment_date: self.encashment_date = getdate(nowdate()) + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + + def before_submit(self): if self.encashment_amount <= 0: frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount")) @@ -30,6 +36,7 @@ class LeaveEncashment(Document): additional_salary = frappe.new_doc("Additional Salary") additional_salary.company = frappe.get_value("Employee", self.employee, "company") additional_salary.employee = self.employee + additional_salary.currency = self.currency earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") if not earning_component: frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type)) diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index bbee18bb0a..aafc9642d4 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -48,6 +48,10 @@ class TestLeaveEncashment(unittest.TestCase): frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]).grant_leave_alloc_for_employee() + def tearDown(self): + for dt in ["Leave Period", "Leave Allocation", "Leave Ledger Entry", "Additional Salary", "Leave Encashment", "Salary Structure", "Leave Policy"]: + frappe.db.sql("delete from `tab%s`" % dt) + def test_leave_balance_value_and_amount(self): frappe.db.sql('''delete from `tabLeave Encashment`''') leave_encashment = frappe.get_doc(dict( @@ -55,7 +59,8 @@ class TestLeaveEncashment(unittest.TestCase): employee=self.employee, leave_type="_Test Leave Type Encashment", leave_period=self.leave_period.name, - payroll_date=today() + payroll_date=today(), + currency="INR" )).insert() self.assertEqual(leave_encashment.leave_balance, 10) @@ -75,7 +80,8 @@ class TestLeaveEncashment(unittest.TestCase): employee=self.employee, leave_type="_Test Leave Type Encashment", leave_period=self.leave_period.name, - payroll_date=today() + payroll_date=today(), + currency="INR" )).insert() leave_encashment.submit() diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index d468f52bc0..acf09f5c03 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -26,11 +26,11 @@ "disbursed_amount", "column_break_11", "maximum_loan_amount", - "is_term_loan", "repayment_method", "repayment_periods", "monthly_repayment_amount", "repayment_start_date", + "is_term_loan", "account_info", "mode_of_payment", "payment_account", diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 8405d6ec62..cd40a665d4 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -13,6 +13,8 @@ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calcul class Loan(AccountsController): def validate(self): + if self.applicant_type == 'Employee' and self.repay_from_salary: + validate_employee_currency_with_company_currency(self.applicant, self.company) self.set_loan_amount() self.validate_loan_amount() self.set_missing_fields() @@ -329,5 +331,14 @@ def create_loan_security_unpledge(unpledge_map, loan, company, applicant_type, a return unpledge_request - - +def validate_employee_currency_with_company_currency(applicant, company): + from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_employee_currency + if not applicant: + frappe.throw(_("Please select Applicant")) + if not company: + frappe.throw(_("Please select Company")) + employee_currency = get_employee_currency(applicant) + company_currency = erpnext.get_company_currency(company) + if employee_currency != company_currency: + frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}") + .format(applicant, employee_currency)) diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 10a7b1143d..a63d06590f 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -19,6 +19,7 @@ from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpled from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure class TestLoan(unittest.TestCase): def setUp(self): @@ -44,6 +45,7 @@ class TestLoan(unittest.TestCase): create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24))) self.applicant1 = make_employee("robert_loan@loan.com") + make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant1, currency='INR') if not frappe.db.exists("Customer", "_Test Loan Customer"): frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True) diff --git a/erpnext/loan_management/doctype/loan_application/test_loan_application.py b/erpnext/loan_management/doctype/loan_application/test_loan_application.py index 687c58000e..2a659e9fc2 100644 --- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee, make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts class TestLoanApplication(unittest.TestCase): @@ -14,6 +14,7 @@ class TestLoanApplication(unittest.TestCase): create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC', 'Interest Income Account - _TC', 'Penalty Income Account - _TC', 'Repay Over Number of Periods', 18) self.applicant = make_employee("kate_loan@loan.com", "_Test Company") + make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant, currency='INR') self.create_loan_application() def create_loan_application(self): @@ -29,7 +30,6 @@ class TestLoanApplication(unittest.TestCase): }) loan_application.insert() - def test_loan_totals(self): loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant}) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 98b2fcdcab..61aa2eec59 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -732,7 +732,9 @@ erpnext.patches.v13_0.set_youtube_video_id erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail +erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.update_reason_for_resignation_in_employee erpnext.patches.v13_0.update_custom_fields_for_shopify execute:frappe.delete_doc("Report", "Quoted Item Comparison") +erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py index c51c38182c..a908c16715 100644 --- a/erpnext/patches/v11_0/create_salary_structure_assignments.py +++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py @@ -8,8 +8,8 @@ from frappe.utils import getdate from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment def execute(): - frappe.reload_doc('Payroll', 'doctype', 'salary_structure') - frappe.reload_doc("Payroll", "doctype", "salary_structure_assignment") + frappe.reload_doc('Payroll', 'doctype', 'Salary Structure') + frappe.reload_doc("Payroll", "doctype", "Salary Structure Assignment") frappe.db.sql(""" delete from `tabSalary Structure Assignment` where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1) @@ -33,6 +33,13 @@ def execute(): AND employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left') """.format(cols), as_dict=1) + all_companies = frappe.db.get_all("Company", fields=["name", "default_currency"]) + for d in all_companies: + company = d.name + company_currency = d.default_currency + + frappe.db.sql("""update `tabSalary Structure` set currency = %s where company=%s""", (company_currency, company)) + for d in ss_details: try: joining_date, relieving_date = frappe.db.get_value("Employee", d.employee, @@ -42,6 +49,7 @@ def execute(): from_date = joining_date elif relieving_date and getdate(from_date) > relieving_date: continue + company_currency = frappe.db.get_value('Company', d.company, 'default_currency') s = frappe.new_doc("Salary Structure Assignment") s.employee = d.employee @@ -52,6 +60,7 @@ def execute(): s.base = d.get("base") s.variable = d.get("variable") s.company = d.company + s.currency = company_currency # to migrate the data of the old employees s.flags.old_employee = True diff --git a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py new file mode 100644 index 0000000000..340bf4947b --- /dev/null +++ b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py @@ -0,0 +1,136 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe import _ +from frappe.model.utils.rename_field import rename_field + +def execute(): + + frappe.reload_doc('Accounts', 'doctype', 'Salary Component Account') + if frappe.db.has_column('Salary Component Account', 'default_account'): + rename_field("Salary Component Account", "default_account", "account") + + doctype_list = [ + { + 'module':'HR', + 'doctype':'Employee Advance' + }, + { + 'module':'HR', + 'doctype':'Leave Encashment' + }, + { + 'module':'Payroll', + 'doctype':'Additional Salary' + }, + { + 'module':'Payroll', + 'doctype':'Employee Benefit Application' + }, + { + 'module':'Payroll', + 'doctype':'Employee Benefit Claim' + }, + { + 'module':'Payroll', + 'doctype':'Employee Incentive' + }, + { + 'module':'Payroll', + 'doctype':'Employee Tax Exemption Declaration' + }, + { + 'module':'Payroll', + 'doctype':'Employee Tax Exemption Proof Submission' + }, + { + 'module':'Payroll', + 'doctype':'Income Tax Slab' + }, + { + 'module':'Payroll', + 'doctype':'Payroll Entry' + }, + { + 'module':'Payroll', + 'doctype':'Retention Bonus' + }, + { + 'module':'Payroll', + 'doctype':'Salary Structure' + }, + { + 'module':'Payroll', + 'doctype':'Salary Structure Assignment' + }, + { + 'module':'Payroll', + 'doctype':'Salary Slip' + }, + ] + + for item in doctype_list: + frappe.reload_doc(item['module'], 'doctype', item['doctype']) + + # update company in employee advance based on employee company + for dt in ['Employee Incentive', 'Leave Encashment', 'Employee Benefit Application', 'Employee Benefit Claim']: + frappe.db.sql(""" + update `tab{doctype}` + set company = (select company from tabEmployee where name=`tab{doctype}`.employee) + """.format(doctype=dt)) + + # update exchange rate for employee advance + frappe.db.sql("update `tabEmployee Advance` set exchange_rate=1") + + # get all companies and it's currency + all_companies = frappe.db.get_all("Company", fields=["name", "default_currency", "default_payroll_payable_account"]) + for d in all_companies: + company = d.name + company_currency = d.default_currency + default_payroll_payable_account = d.default_payroll_payable_account + + if not default_payroll_payable_account: + default_payroll_payable_account = frappe.db.get_value("Account", + {"account_name": _("Payroll Payable"), "company": company, "account_currency": company_currency, "is_group": 0}) + + # update currency in following doctypes based on company currency + doctypes_for_currency = ['Employee Advance', 'Leave Encashment', 'Employee Benefit Application', + 'Employee Benefit Claim', 'Employee Incentive', 'Additional Salary', + 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission', + 'Income Tax Slab', 'Retention Bonus', 'Salary Structure'] + + for dt in doctypes_for_currency: + frappe.db.sql("""update `tab{doctype}` set currency = %s where company=%s""" + .format(doctype=dt), (company_currency, company)) + + # update fields in payroll entry + frappe.db.sql(""" + update `tabPayroll Entry` + set currency = %s, + exchange_rate = 1, + payroll_payable_account=%s + where company=%s + """, (company_currency, default_payroll_payable_account, company)) + + # update fields in Salary Structure Assignment + frappe.db.sql(""" + update `tabSalary Structure Assignment` + set currency = %s, + payroll_payable_account=%s + where company=%s + """, (company_currency, default_payroll_payable_account, company)) + + # update fields in Salary Slip + frappe.db.sql(""" + update `tabSalary Slip` + set currency = %s, + exchange_rate = 1, + base_hour_rate = hour_rate, + base_gross_pay = gross_pay, + base_total_deduction = total_deduction, + base_net_pay = net_pay, + base_rounded_total = rounded_total, + base_total_in_words = total_in_words + where company=%s + """, (company_currency, company)) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js index d56cd4e967..0784de93eb 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.js +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js @@ -12,5 +12,57 @@ frappe.ui.form.on('Additional Salary', { } }; }); + + if (!frm.doc.currency) return; + frm.set_query("salary_component", function() { + return { + query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {currency: frm.doc.currency, company: frm.doc.company} + }; + }); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_company') + ]); + } else { + frm.set_value("company", null); + } + }, + + set_company: function(frm) { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Employee", + fieldname: "company", + filters: { + name: frm.doc.employee + } + }, + callback: function(data) { + if (data.message) { + frm.set_value("company", data.message.company); + } + } + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); }, }); diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json index 69cb5da893..2b29f667fb 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.json +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json @@ -11,20 +11,21 @@ "employee", "employee_name", "salary_component", - "overwrite_salary_structure_amount", - "deduct_full_tax_on_selected_payroll_date", + "type", + "amount", "ref_doctype", "ref_docname", + "amended_from", "column_break_5", "company", - "is_recurring", + "department", + "currency", "from_date", "to_date", "payroll_date", - "type", - "department", - "amount", - "amended_from" + "is_recurring", + "overwrite_salary_structure_amount", + "deduct_full_tax_on_selected_payroll_date" ], "fields": [ { @@ -59,6 +60,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "reqd": 1 }, { @@ -159,11 +161,22 @@ "label": "Reference Document", "options": "ref_doctype", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 21:10:50.374063", + "modified": "2020-10-20 17:51:13.419716", "modified_by": "Administrator", "module": "Payroll", "name": "Additional Salary", diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index e3dc9070ec..f5af677fce 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -22,10 +22,15 @@ class AdditionalSalary(Document): def validate(self): self.validate_dates() + self.validate_salary_structure() self.validate_recurring_additional_salary_overlap() if self.amount < 0: frappe.throw(_("Amount should not be less than zero.")) + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + def validate_recurring_additional_salary_overlap(self): if self.is_recurring: additional_salaries = frappe.db.sql(""" diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py index de26543b57..4d47f25fcf 100644 --- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py @@ -8,6 +8,7 @@ from frappe.utils import nowdate, add_days from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure class TestAdditionalSalary(unittest.TestCase): @@ -15,12 +16,19 @@ class TestAdditionalSalary(unittest.TestCase): def setUp(self): setup_test() + def tearDown(self): + for dt in ["Salary Slip", "Additional Salary", "Salary Structure Assignment", "Salary Structure"]: + frappe.db.sql("delete from `tab%s`" % dt) + def test_recurring_additional_salary(self): + amount = 0 + salary_component = None emp_id = make_employee("test_additional@salary.com") frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800)) + salary_structure = make_salary_structure("Test Salary Structure Additional Salary", "Monthly", employee=emp_id) add_sal = get_additional_salary(emp_id) - - ss = make_employee_salary_slip("test_additional@salary.com", "Monthly") + + ss = make_employee_salary_slip("test_additional@salary.com", "Monthly", salary_structure=salary_structure.name) for earning in ss.earnings: if earning.salary_component == "Recurring Salary Component": amount = earning.amount @@ -29,8 +37,6 @@ class TestAdditionalSalary(unittest.TestCase): self.assertEqual(amount, add_sal.amount) self.assertEqual(salary_component, add_sal.salary_component) - - def get_additional_salary(emp_id): create_salary_component("Recurring Salary Component") add_sal = frappe.new_doc("Additional Salary") @@ -40,6 +46,7 @@ def get_additional_salary(emp_id): add_sal.from_date = add_days(nowdate(), -50) add_sal.to_date = add_days(nowdate(), 180) add_sal.amount = 5000 + add_sal.currency = erpnext.get_default_currency() add_sal.save() add_sal.submit() diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js index f509df31e8..6756cd93e7 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js @@ -3,7 +3,12 @@ frappe.ui.form.on('Employee Benefit Application', { employee: function(frm) { - frm.trigger('set_earning_component'); + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_earning_component') + ]); + } var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; @@ -38,9 +43,26 @@ frappe.ui.form.on('Employee Benefit Application', { }); }, + get_employee_currency: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } + }, + payroll_period: function(frm) { var method, args; - if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ + if (frm.doc.employee && frm.doc.date && frm.doc.payroll_period) { method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; args = { employee: frm.doc.employee, @@ -60,11 +82,14 @@ var get_max_benefits=function(frm, method, args) { method: method, args: args, callback: function (data) { - if(!data.exc){ - if(data.message){ + if (!data.exc) { + if (data.message) { frm.set_value("max_benefits", data.message); + } else { + frm.set_value("max_benefits", 0); } } + frm.refresh_fields(); } }); }; @@ -82,14 +107,19 @@ var calculate_all = function(doc) { var tbl = doc.employee_benefits || []; var pro_rata_dispensed_amount = 0; var total_amount = 0; - for(var i = 0; i < tbl.length; i++){ - if(cint(tbl[i].amount) > 0) { - total_amount += flt(tbl[i].amount); - } - if(tbl[i].pay_against_benefit_claim != 1){ - pro_rata_dispensed_amount += flt(tbl[i].amount); + if (doc.max_benefits === 0) { + doc.employee_benefits = []; + } else { + for (var i = 0; i < tbl.length; i++) { + if (cint(tbl[i].amount) > 0) { + total_amount += flt(tbl[i].amount); + } + if (tbl[i].pay_against_benefit_claim != 1) { + pro_rata_dispensed_amount += flt(tbl[i].amount); + } } } + doc.total_amount = total_amount; doc.remaining_benefit = doc.max_benefits - total_amount; doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json index b0c1bd6c3e..9a5a463152 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -10,12 +10,14 @@ "field_order": [ "employee", "employee_name", + "currency", "max_benefits", "remaining_benefit", "column_break_2", "date", "payroll_period", "department", + "company", "amended_from", "section_break_4", "employee_benefits", @@ -43,12 +45,14 @@ "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Yearly)", + "options": "currency", "read_only": 1 }, { "fieldname": "remaining_benefit", "fieldtype": "Currency", "label": "Remaining Benefits (Yearly)", + "options": "currency", "read_only": 1 }, { @@ -108,18 +112,38 @@ "fieldname": "total_amount", "fieldtype": "Currency", "label": "Total Amount", + "options": "currency", "read_only": 1 }, { "fieldname": "pro_rata_dispensed_amount", "fieldtype": "Currency", "label": "Dispensed Amount (Pro-rated)", + "options": "currency", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:58:31.271922", + "modified": "2020-11-25 11:49:05.095101", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application", diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index ef844fbd3b..27df30a459 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -33,8 +33,8 @@ class EmployeeBenefitApplication(Document): benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component) benefit_claim_remining = benefit_claimed - benefit_given if benefit_claimed > 0 and benefit_claim_remining > benefit.amount: - frappe.throw(_("An amount of {0} already claimed for the component {1},\ - set the amount equal or greater than {2}").format(benefit_claimed, benefit.earning_component, benefit_claim_remining)) + frappe.throw(_("An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}").format( + benefit_claimed, benefit.earning_component, benefit_claim_remining)) def validate_remaining_benefit_amount(self): # check salary structure earnings have flexi component (sum of max_benefit_amount) @@ -62,11 +62,11 @@ class EmployeeBenefitApplication(Document): if pro_rata_amount == 0 and non_pro_rata_amount == 0: frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit)) elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit): - frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \ - as pro-rata component").format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount)) + frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component").format( + non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount)) elif non_pro_rata_amount == 0: - frappe.throw(_("Please add the remaining benefits {0} to the application as \ - pro-rata component").format(self.remaining_benefit)) + frappe.throw(_("Please add the remaining benefits {0} to the application as pro-rata component").format( + self.remaining_benefit)) def validate_max_benefit_for_component(self): if self.employee_benefits: @@ -115,7 +115,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period): if max_benefits and max_benefits > 0: have_depends_on_payment_days = False per_day_amount_total = 0 - payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0] + payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[1] payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) # Get all salary slip flexi amount in the payroll period @@ -239,4 +239,17 @@ def get_earning_components(doctype, txt, searchfield, start, page_len, filters): """, salary_structure) else: frappe.throw(_("Salary Structure not found for employee {0} and date {1}") - .format(filters['employee'], filters['date'])) \ No newline at end of file + .format(filters['employee'], filters['date'])) + +@frappe.whitelist() +def get_earning_components_max_benefits(employee, date, earning_component): + salary_structure = get_assigned_salary_structure(employee, date) + amount = frappe.db.sql(""" + select amount + from `tabSalary Detail` + where parent = %s and is_flexible_benefit = 1 + and salary_component = %s + order by name + """, salary_structure, earning_component) + + return amount if amount else 0 \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json index fa6b4da2af..c93d356c20 100644 --- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -33,6 +33,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Max Benefit Amount", + "options": "currency", "read_only": 1 }, { @@ -40,12 +41,13 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "reqd": 1 } ], "istable": 1, "links": [], - "modified": "2020-06-22 23:45:00.519134", + "modified": "2020-09-29 16:22:15.783854", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application Detail", diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js index 6db6cb86b3..ea9ccd5205 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -12,5 +12,24 @@ frappe.ui.form.on('Employee Benefit Claim', { }, employee: function(frm) { frm.set_value("earning_component", null); + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + } + } + }); + } + if (!frm.doc.earning_component) { + frm.doc.max_amount_eligible = null; + frm.doc.claimed_amount = null; + } + frm.refresh_fields(); } }); diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json index ae4c218615..da24aacda1 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -12,6 +12,8 @@ "department", "column_break_3", "claim_date", + "currency", + "company", "benefit_type_and_amount", "earning_component", "max_amount_eligible", @@ -76,6 +78,7 @@ "fieldname": "max_amount_eligible", "fieldtype": "Currency", "label": "Max Amount Eligible", + "options": "currency", "read_only": 1 }, { @@ -92,6 +95,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Claimed Amount", + "options": "currency", "reqd": 1 }, { @@ -119,11 +123,29 @@ "fieldname": "attachments", "fieldtype": "Attach", "label": "Attachments" + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 23:01:50.791676", + "modified": "2020-11-25 11:49:56.097352", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Claim", diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js index db0f83aac9..85d1c54a22 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js @@ -11,12 +11,57 @@ frappe.ui.form.on('Employee Incentive', { }; }); + if (!frm.doc.currency) return; frm.set_query("salary_component", function() { return { - filters: { - "type": "Earning" - } + query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} }; }); - } + + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.run_serially([ + () => frm.trigger('get_employee_currency'), + () => frm.trigger('set_company') + ]); + } else { + frm.set_value("company", null); + } + }, + + set_company: function(frm) { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Employee", + fieldname: "company", + filters: { + name: frm.doc.employee + } + }, + callback: function(data) { + if (data.message) { + frm.set_value("company", data.message.company); + } + } + }); + }, + + get_employee_currency: function(frm) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + }, }); diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json index 204c9a40b1..e5b1052b3a 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json @@ -7,10 +7,12 @@ "engine": "InnoDB", "field_order": [ "employee", - "incentive_amount", "employee_name", - "salary_component", + "company", + "currency", + "incentive_amount", "column_break_5", + "salary_component", "payroll_date", "department", "amended_from" @@ -28,6 +30,7 @@ "fieldname": "incentive_amount", "fieldtype": "Currency", "label": "Incentive Amount", + "options": "currency", "reqd": 1 }, { @@ -70,11 +73,29 @@ "label": "Salary Component", "options": "Salary Component", "reqd": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:42:51.209630", + "modified": "2020-10-20 17:22:16.468042", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Incentive", diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index 84a97f6bb2..ead3db126f 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -4,14 +4,23 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class EmployeeIncentive(Document): + def validate(self): + self.validate_salary_structure() + + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + def on_submit(self): company = frappe.db.get_value('Employee', self.employee, 'company') additional_salary = frappe.new_doc('Additional Salary') additional_salary.employee = self.employee + additional_salary.currency = self.currency additional_salary.salary_component = self.salary_component additional_salary.overwrite_salary_structure_amount = 0 additional_salary.amount = self.incentive_amount diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index de7c348bb2..83d4ae53df 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -14,6 +14,7 @@ "column_break_2", "payroll_period", "company", + "currency", "amended_from", "section_break_8", "declarations", @@ -92,6 +93,7 @@ "fieldname": "total_declared_amount", "fieldtype": "Currency", "label": "Total Declared Amount", + "options": "currency", "read_only": 1 }, { @@ -102,12 +104,22 @@ "fieldname": "total_exemption_amount", "fieldtype": "Currency", "label": "Total Exemption Amount", + "options": "currency", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:49:43.829892", + "modified": "2020-10-20 16:42:24.493761", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 9549fd1b75..0609d19149 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -22,6 +22,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), "declarations": [ dict(exemption_sub_category = "_Test Sub Category", exemption_category = "_Test Category", @@ -39,6 +40,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), "declarations": [ dict(exemption_sub_category = "_Test Sub Category", exemption_category = "_Test Category", @@ -54,6 +56,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), "declarations": [ dict(exemption_sub_category = "_Test Sub Category", exemption_category = "_Test Category", @@ -70,6 +73,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", + "currency": erpnext.get_default_currency(), "declarations": [ dict(exemption_sub_category = "_Test Sub Category", exemption_category = "_Test Category", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json index 8c2f9aa370..723a3df3c7 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -35,6 +35,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Maximum Exempted Amount", + "options": "currency", "read_only": 1, "reqd": 1 }, @@ -43,12 +44,13 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Declared Amount", + "options": "currency", "reqd": 1 } ], "istable": 1, "links": [], - "modified": "2020-06-22 23:41:03.638739", + "modified": "2020-10-20 16:43:09.606265", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Declaration Category", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js index 715d7553b0..497f35c41e 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -54,5 +54,9 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { }); }); } + }, + + currency: function(frm) { + frm.refresh_fields(); } }); diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index b62b5aab0b..53f18cb1fe 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -11,6 +11,7 @@ "employee", "employee_name", "department", + "currency", "column_break_2", "submission_date", "payroll_period", @@ -97,6 +98,7 @@ "fieldname": "total_actual_amount", "fieldtype": "Currency", "label": "Total Actual Amount", + "options": "currency", "read_only": 1 }, { @@ -107,6 +109,7 @@ "fieldname": "exemption_amount", "fieldtype": "Currency", "label": "Total Exemption Amount", + "options": "currency", "read_only": 1 }, { @@ -126,11 +129,20 @@ "options": "Employee Tax Exemption Proof Submission", "print_hide": 1, "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:53:10.412321", + "modified": "2020-10-20 16:47:03.410020", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Proof Submission", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json index c1f532050a..2fd8b94efd 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -34,6 +34,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Maximum Exemption Amount", + "options": "currency", "read_only": 1, "reqd": 1 }, @@ -48,12 +49,13 @@ "fieldname": "amount", "fieldtype": "Currency", "in_list_view": 1, - "label": "Actual Amount" + "label": "Actual Amount", + "options": "currency" } ], "istable": 1, "links": [], - "modified": "2020-06-22 23:37:08.265600", + "modified": "2020-10-20 16:47:31.480870", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Proof Submission Detail", diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js index 73a54eb8dd..7d780d3b04 100644 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js @@ -2,5 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on('Income Tax Slab', { - + currency: function(frm) { + frm.refresh_fields(); + } }); diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json index 6337d5a6d3..9fa261dea2 100644 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json @@ -9,8 +9,9 @@ "effective_from", "company", "column_break_3", - "allow_tax_exemption", + "currency", "standard_tax_exemption_amount", + "allow_tax_exemption", "disabled", "amended_from", "taxable_salary_slabs_section", @@ -70,7 +71,7 @@ "fieldname": "standard_tax_exemption_amount", "fieldtype": "Currency", "label": "Standard Tax Exemption Amount", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "company", @@ -90,11 +91,20 @@ "fieldtype": "Table", "label": "Other Taxes and Charges", "options": "Income Tax Slab Other Charges" + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 20:27:13.425084", + "modified": "2020-10-19 13:54:24.728075", "modified_by": "Administrator", "module": "Payroll", "name": "Income Tax Slab", diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json index 7f21204591..0dba338250 100644 --- a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json +++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -45,7 +45,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Min Taxable Income", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "column_break_7", @@ -57,12 +57,12 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Max Taxable Income", - "options": "Company:company:default_currency" + "options": "currency" } ], "istable": 1, "links": [], - "modified": "2020-06-22 23:33:17.931912", + "modified": "2020-10-19 13:45:12.850090", "modified_by": "Administrator", "module": "Payroll", "name": "Income Tax Slab Other Charges", diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json index bb68e1814a..8a55224dca 100644 --- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json +++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -52,7 +52,7 @@ ], "istable": 1, "links": [], - "modified": "2020-06-22 23:25:13.779032", + "modified": "2020-09-30 12:40:07.999878", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Employee Detail", diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 1abc869c53..cb48abbc36 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -17,6 +17,16 @@ frappe.ui.form.on('Payroll Entry', { } }; }); + + frm.set_query("payroll_payable_account", function() { + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + } + }; + }); }, refresh: function(frm) { @@ -139,6 +149,36 @@ frappe.ui.form.on('Payroll Entry', { frm.events.clear_employee_table(frm); }, + currency: function (frm) { + var company_currency; + if (!frm.doc.company) { + company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } else { + company_currency = erpnext.get_currency(frm.doc.company); + } + if (frm.doc.currency) { + if (company_currency != frm.doc.currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: frm.doc.currency, + to_currency: company_currency, + }, + callback: function(r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + }); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + } + } + }, + department: function (frm) { frm.events.clear_employee_table(frm); }, diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json index 31a899699d..7a48dd1475 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json @@ -11,8 +11,11 @@ "column_break0", "posting_date", "payroll_frequency", - "column_break1", "company", + "column_break1", + "currency", + "exchange_rate", + "payroll_payable_account", "section_break_8", "branch", "department", @@ -257,12 +260,37 @@ { "fieldname": "column_break_33", "fieldtype": "Column Break" + }, + { + "depends_on": "company", + "fieldname": "currency", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "options": "Account", + "reqd": 1 } ], "icon": "fa fa-cog", "is_submittable": 1, "links": [], - "modified": "2020-06-22 20:06:06.953904", + "modified": "2020-10-23 13:00:33.753228", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Entry", diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index a3d12c35c0..67ee231e40 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.model.document import Document from dateutil.relativedelta import relativedelta from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT, date_diff @@ -51,13 +51,15 @@ class PayrollEntry(Document): where docstatus = 1 and is_active = 'Yes' - and company = %(company)s and + and company = %(company)s + and currency = %(currency)s and 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}) + {"company": self.company, "currency": self.currency, "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 t2.payroll_payable_account = %(payroll_payable_account)s " cond += "and %(from_date)s >= t2.from_date" emp_list = frappe.db.sql(""" select @@ -68,14 +70,26 @@ class PayrollEntry(Document): t1.name = t2.employee and t2.docstatus = 1 %s order by t2.from_date desc - """ % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date}, as_dict=True) + """ % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "payroll_payable_account": self.payroll_payable_account}, as_dict=True) return emp_list def fill_employee_details(self): self.set('employees', []) employees = self.get_emp_list() if not employees: - frappe.throw(_("No employees for the mentioned criteria")) + error_msg = _("No employees found for the mentioned criteria:
Company: {0}
Currency: {1}
Payroll Payable Account: {2}").format( + frappe.bold(self.company), frappe.bold(self.currency), frappe.bold(self.payroll_payable_account)) + if self.branch: + error_msg += "
" + _("Branch: {0}").format(frappe.bold(self.branch)) + if self.department: + error_msg += "
" + _("Department: {0}").format(frappe.bold(self.department)) + if self.designation: + error_msg += "
" + _("Designation: {0}").format(frappe.bold(self.designation)) + if self.start_date: + error_msg += "
" + _("Start date: {0}").format(frappe.bold(self.start_date)) + if self.end_date: + error_msg += "
" + _("End date: {0}").format(frappe.bold(self.end_date)) + frappe.throw(error_msg, title=_("No employees found")) for d in employees: self.append('employees', d) @@ -123,7 +137,9 @@ class PayrollEntry(Document): "posting_date": self.posting_date, "deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits, "deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof, - "payroll_entry": self.name + "payroll_entry": self.name, + "exchange_rate": self.exchange_rate, + "currency": self.currency }) if len(emp_list) > 30: frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=emp_list, args=args) @@ -160,10 +176,10 @@ class PayrollEntry(Document): def get_salary_component_account(self, salary_component): account = frappe.db.get_value("Salary Component Account", - {"parent": salary_component, "company": self.company}, "default_account") + {"parent": salary_component, "company": self.company}, "account") if not account: - frappe.throw(_("Please set default account in Salary Component {0}") + frappe.throw(_("Please set account in Salary Component {0}") .format(salary_component)) return account @@ -203,21 +219,11 @@ class PayrollEntry(Document): account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount return account_dict - def get_default_payroll_payable_account(self): - payroll_payable_account = frappe.get_cached_value('Company', - {"company_name": self.company}, "default_payroll_payable_account") - - if not payroll_payable_account: - frappe.throw(_("Please set Default Payroll Payable Account in Company {0}") - .format(self.company)) - - return payroll_payable_account - def make_accrual_jv_entry(self): self.check_permission('write') earnings = self.get_salary_component_total(component_type = "earnings") or {} deductions = self.get_salary_component_total(component_type = "deductions") or {} - default_payroll_payable_account = self.get_default_payroll_payable_account() + payroll_payable_account = self.payroll_payable_account jv_name = "" precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") @@ -230,14 +236,19 @@ class PayrollEntry(Document): journal_entry.posting_date = self.posting_date accounts = [] + currencies = [] payable_amount = 0 + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) # Earnings for acc_cc, amount in earnings.items(): + exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies) payable_amount += flt(amount, precision) accounts.append({ "account": acc_cc[0], - "debit_in_account_currency": flt(amount, precision), + "debit_in_account_currency": flt(amt, precision), + "exchange_rate": flt(exchange_rate), "party_type": '', "cost_center": acc_cc[1] or self.cost_center, "project": self.project @@ -245,25 +256,32 @@ class PayrollEntry(Document): # Deductions for acc_cc, amount in deductions.items(): + exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies) payable_amount -= flt(amount, precision) accounts.append({ "account": acc_cc[0], - "credit_in_account_currency": flt(amount, precision), + "credit_in_account_currency": flt(amt, precision), + "exchange_rate": flt(exchange_rate), "cost_center": acc_cc[1] or self.cost_center, "party_type": '', "project": self.project }) # Payable amount + exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies) accounts.append({ - "account": default_payroll_payable_account, - "credit_in_account_currency": flt(payable_amount, precision), + "account": payroll_payable_account, + "credit_in_account_currency": flt(payable_amt, precision), + "exchange_rate": flt(exchange_rate), "party_type": '', "cost_center": self.cost_center }) journal_entry.set("accounts", accounts) - journal_entry.title = default_payroll_payable_account + if len(currencies) > 1: + multi_currency = 1 + journal_entry.multi_currency = multi_currency + journal_entry.title = payroll_payable_account journal_entry.save() try: @@ -275,6 +293,18 @@ class PayrollEntry(Document): return jv_name + def get_amount_and_exchange_rate_for_journal_entry(self, account, amount, company_currency, currencies): + conversion_rate = 1 + exchange_rate = self.exchange_rate + account_currency = frappe.db.get_value('Account', account, 'account_currency') + if account_currency not in currencies: + currencies.append(account_currency) + if account_currency == company_currency: + conversion_rate = self.exchange_rate + exchange_rate = 1 + amount = flt(amount) * flt(conversion_rate) + return exchange_rate, amount + def make_payment_entry(self): self.check_permission('write') @@ -303,31 +333,43 @@ class PayrollEntry(Document): self.create_journal_entry(salary_slip_total, "salary") def create_journal_entry(self, je_payment_amount, user_remark): - default_payroll_payable_account = self.get_default_payroll_payable_account() + payroll_payable_account = self.payroll_payable_account precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") + accounts = [] + currencies = [] + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) + + exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies) + accounts.append({ + "account": self.payment_account, + "bank_account": self.bank_account, + "credit_in_account_currency": flt(amount, precision), + "exchange_rate": flt(exchange_rate), + }) + + exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies) + accounts.append({ + "account": payroll_payable_account, + "debit_in_account_currency": flt(amount, precision), + "exchange_rate": flt(exchange_rate), + "reference_type": self.doctype, + "reference_name": self.name + }) + + if len(currencies) > 1: + multi_currency = 1 + journal_entry = frappe.new_doc('Journal Entry') journal_entry.voucher_type = 'Bank Entry' journal_entry.user_remark = _('Payment of {0} from {1} to {2}')\ .format(user_remark, self.start_date, self.end_date) journal_entry.company = self.company journal_entry.posting_date = self.posting_date + journal_entry.multi_currency = multi_currency - payment_amount = flt(je_payment_amount, precision) - - journal_entry.set("accounts", [ - { - "account": self.payment_account, - "bank_account": self.bank_account, - "credit_in_account_currency": payment_amount - }, - { - "account": default_payroll_payable_account, - "debit_in_account_currency": payment_amount, - "reference_type": self.doctype, - "reference_name": self.name - } - ]) + journal_entry.set("accounts", accounts) journal_entry.save(ignore_permissions = True) def update_salary_slip_status(self, jv_name = None): @@ -496,6 +538,21 @@ def create_salary_slips_for_employees(employees, args, publish_progress=True): if publish_progress: frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)), title = _("Creating Salary Slips...")) + else: + salary_slip_name = frappe.db.sql( + '''SELECT + name + FROM `tabSalary Slip` + WHERE company=%s + AND start_date >= %s + AND end_date <= %s + AND employee = %s + ''', (args.company, args.start_date, args.end_date, emp), as_dict=True) + + salary_slip_doc = frappe.get_doc('Salary Slip', salary_slip_name[0].name) + salary_slip_doc.exchange_rate = args.exchange_rate + salary_slip_doc.set_totals() + salary_slip_doc.db_update() payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry) payroll_entry.db_set("salary_slips_created", 1) diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index b0f225d909..54106c8d16 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -10,8 +10,8 @@ from frappe.utils import add_months from erpnext.payroll.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.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ - make_earning_salary_component, make_deduction_salary_component, create_account -from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment 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 @@ -34,10 +34,47 @@ 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, company=company) + company_doc = frappe.get_doc('Company', company) + make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company, currency=company_doc.default_currency) 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) + make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency) + + def test_multi_currency_payroll_entry(self): # pylint: disable=no-self-use + company = erpnext.get_default_company() + employee = make_employee("test_muti_currency_employee@payroll.com", company=company) + for data in frappe.get_all('Salary Component', fields = ["name"]): + if not frappe.db.get_value('Salary Component Account', + {'parent': data.name, 'company': company}, 'name'): + get_salary_component_account(data.name) + + company_doc = frappe.get_doc('Company', company) + salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD') + create_salary_structure_assignment(employee, salary_structure.name, company=company) + frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"}))) + salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure") + dates = get_start_end_dates('Monthly', nowdate()) + payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, + payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70) + payroll_entry.make_payment_entry() + + salary_slip.load_from_db() + + payroll_je = salary_slip.journal_entry + payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je) + + self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit) + self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit) + + payment_entry = frappe.db.sql(''' + Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea + Where je.name = jea.parent + And jea.reference_name = %s + ''', (payroll_entry.name), as_dict=1) + + self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit) + self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit) def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use for data in frappe.get_all('Salary Component', fields = ["name"]): @@ -52,24 +89,32 @@ class TestPayrollEntry(unittest.TestCase): "company": "_Test Company" }).insert() + frappe.db.sql("""delete from `tabEmployee` where employee_name='test_employee1@example.com' """) + frappe.db.sql("""delete from `tabEmployee` where employee_name='test_employee2@example.com' """) + frappe.db.sql("""delete from `tabSalary Structure` where name='_Test Salary Structure 1' """) + frappe.db.sql("""delete from `tabSalary Structure` where name='_Test Salary Structure 2' """) + 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") + create_account(account_name="_Test Payroll Payable", + company="_Test Company", parent_account="Current Liabilities - _TC") + + if not frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") or \ + frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") != "_Test Payroll Payable - _TC": + frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account", + "_Test Payroll Payable - _TC") + + make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency")) + make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency")) 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") + pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date, payable_account="_Test Payroll Payable - _TC", + currency=frappe.db.get_value("Company", "_Test Company", "default_currency"), 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 @@ -121,7 +166,7 @@ class TestPayrollEntry(unittest.TestCase): employee_doc.save() salary_structure = "Test Salary Structure for Loan" - make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company") + make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency) loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1)) loan.repay_from_salary = 1 @@ -133,8 +178,8 @@ class TestPayrollEntry(unittest.TestCase): dates = get_start_end_dates('Monthly', nowdate()) - make_payroll_entry(company="_Test Company", start_date=dates.start_date, - end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC") + make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account, + currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC") name = frappe.db.get_value('Salary Slip', {'posting_date': nowdate(), 'employee': applicant}, 'name') @@ -165,6 +210,9 @@ def make_payroll_entry(**args): payroll_entry.payroll_frequency = "Monthly" payroll_entry.branch = args.branch or None payroll_entry.department = args.department or None + payroll_entry.payroll_payable_account = args.payable_account + payroll_entry.currency = args.currency + payroll_entry.exchange_rate = args.exchange_rate or 1 if args.cost_center: payroll_entry.cost_center = args.cost_center @@ -212,3 +260,11 @@ def make_holiday(holiday_list_name): }).insert() return holiday_list_name + +def get_salary_slip(user, period, salary_structure): + salary_slip = make_employee_salary_slip(user, period, salary_structure) + salary_slip.exchange_rate = 70 + salary_slip.calculate_net_pay() + salary_slip.db_update() + + return salary_slip \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js index 8ff55151f6..092cbd8974 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js +++ b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js @@ -9,45 +9,45 @@ QUnit.test("test: Set Salary Components", function (assert) { () => { var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); row.company = 'For Testing'; - row.default_account = 'Salary - FT'; + row.account = 'Salary - FT'; }, () => cur_frm.save(), () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'), + () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), () => frappe.set_route('Form', 'Salary Component', 'Basic'), () => { var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); row.company = 'For Testing'; - row.default_account = 'Salary - FT'; + row.account = 'Salary - FT'; }, () => cur_frm.save(), () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'), + () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), () => frappe.set_route('Form', 'Salary Component', 'Income Tax'), () => { var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); row.company = 'For Testing'; - row.default_account = 'Salary - FT'; + row.account = 'Salary - FT'; }, () => cur_frm.save(), () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'), + () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), () => frappe.set_route('Form', 'Salary Component', 'Arrear'), () => { var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); row.company = 'For Testing'; - row.default_account = 'Salary - FT'; + row.account = 'Salary - FT'; }, () => cur_frm.save(), () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'), + () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), () => frappe.set_route('Form', 'Company', 'For Testing'), () => cur_frm.set_value('default_payroll_payable_account', 'Payroll Payable - FT'), diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js index 64e726db85..6fe8ccad46 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js @@ -18,5 +18,22 @@ frappe.ui.form.on('Retention Bonus', { } }; }); + }, + + employee: function(frm) { + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if (r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } } }); diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json index da884c2f28..6647230078 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json @@ -17,7 +17,8 @@ "column_break_6", "employee_name", "department", - "date_of_joining" + "date_of_joining", + "currency" ], "fields": [ { @@ -46,6 +47,7 @@ "fieldname": "bonus_amount", "fieldtype": "Currency", "label": "Bonus Amount", + "options": "currency", "reqd": 1 }, { @@ -89,11 +91,22 @@ "label": "Salary Component", "options": "Salary Component", "reqd": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:42:05.251951", + "modified": "2020-10-20 17:27:47.003134", "modified_by": "Administrator", "module": "Payroll", "name": "Retention Bonus", @@ -151,7 +164,6 @@ "share": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js index c455eb3303..dbf75140ac 100644 --- a/erpnext/payroll/doctype/salary_component/salary_component.js +++ b/erpnext/payroll/doctype/salary_component/salary_component.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Salary Component', { setup: function(frm) { - frm.set_query("default_account", "accounts", function(doc, cdt, cdn) { + frm.set_query("account", "accounts", function(doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json index eedb56ec08..5c1eb61281 100644 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -147,7 +147,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "Company:company:default_currency" + "options": "currency" }, { "default": "0", @@ -160,7 +160,7 @@ "fieldname": "default_amount", "fieldtype": "Currency", "label": "Default Amount", - "options": "Company:company:default_currency", + "options": "currency", "print_hide": 1 }, { @@ -169,6 +169,7 @@ "hidden": 1, "label": "Additional Amount", "no_copy": 1, + "options": "currency", "print_hide": 1, "read_only": 1 }, @@ -177,6 +178,7 @@ "fieldname": "tax_on_flexible_benefit", "fieldtype": "Currency", "label": "Tax on flexible benefit", + "options": "currency", "read_only": 1 }, { @@ -184,6 +186,7 @@ "fieldname": "tax_on_additional_salary", "fieldtype": "Currency", "label": "Tax on additional salary", + "options": "currency", "read_only": 1 }, { @@ -227,7 +230,7 @@ ], "istable": 1, "links": [], - "modified": "2020-10-07 20:39:41.619283", + "modified": "2020-11-25 13:12:41.081106", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Detail", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index 0671b570d1..f7e22c6387 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -74,14 +74,85 @@ frappe.ui.form.on("Salary Slip", { if (!frm.doc.letter_head && company.default_letter_head) { frm.set_value('letter_head', company.default_letter_head); } + frm.trigger("set_dynamic_labels"); + }, + + set_dynamic_labels: function(frm) { + var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency"); + frappe.run_serially([ + () => frm.events.set_exchange_rate(frm, company_currency), + () => frm.events.change_form_labels(frm, company_currency), + () => frm.events.change_grid_labels(frm), + () => frm.refresh_fields() + ]); + }, + + set_exchange_rate: function(frm, company_currency) { + if (frm.doc.docstatus === 0) { + if (frm.doc.currency) { + var from_currency = frm.doc.currency; + if (from_currency != company_currency) { + frm.events.hide_loan_section(frm); + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: from_currency, + to_currency: company_currency, + }, + callback: function(r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + } + }); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + } + } + } + }, + + exchange_rate: function(frm) { + calculate_totals(frm); + }, + + hide_loan_section: function(frm) { + frm.set_df_property('section_break_43', 'hidden', 1); + }, + + change_form_labels: function(frm, company_currency) { + frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction", + "base_net_pay", "base_rounded_total", "base_total_in_words"], + company_currency); + + frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words"], + frm.doc.currency); + + // toggle fields + frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction", + "base_net_pay", "base_rounded_total", "base_total_in_words"], + frm.doc.currency != company_currency); + }, + + change_grid_labels: function(frm) { + frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit", + "tax_on_additional_salary"], frm.doc.currency, "earnings"); + + frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit", + "tax_on_additional_salary"], frm.doc.currency, "deductions"); }, refresh: function(frm) { frm.trigger("toggle_fields"); var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"]; - cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false); - cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false); + frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false); + frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false); + calculate_totals(frm); + frm.trigger("set_dynamic_labels"); }, salary_slip_based_on_timesheet: function(frm) { @@ -118,51 +189,94 @@ frappe.ui.form.on("Salary Slip", { }, get_emp_and_working_day_details: function(frm) { - return frappe.call({ - method: 'get_emp_and_working_day_details', - doc: frm.doc, - callback: function(r) { - frm.refresh(); - if (r.message[1] !== "Leave" && r.message[0]) { - frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as ")+ r.message[0] +__(". You can can change this in ") + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)); + if (frm.doc.employee) { + return frappe.call({ + method: 'get_emp_and_working_day_details', + doc: frm.doc, + callback: function(r) { + if (r.message[1] !== "Leave" && r.message[0]) { + frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as {0}. You can can change this in {1}", [r.message, frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)])); + } + frm.refresh(); } - } - }); + }); + } } }); frappe.ui.form.on('Salary Slip Timesheet', { - time_sheet: function(frm, dt, dn) { - total_work_hours(frm, dt, dn); + time_sheet: function(frm) { + calculate_totals(frm); }, - timesheets_remove: function(frm, dt, dn) { - total_work_hours(frm, dt, dn); + timesheets_remove: function(frm) { + calculate_totals(frm); } }); -// calculate total working hours, earnings based on hourly wages and totals -var total_work_hours = function(frm) { - var total_working_hours = 0.0; - $.each(frm.doc["timesheets"] || [], function(i, timesheet) { - total_working_hours += timesheet.working_hours; - }); - frm.set_value('total_working_hours', total_working_hours); - - var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate; - - frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => { - var gross_pay = 0.0; - $.each(frm.doc["earnings"], function(i, earning) { - if (earning.salary_component == r.salary_component) { - earning.amount = wages_amount; - frm.refresh_fields('earnings'); +var calculate_totals = function(frm) { + if (frm.doc.earnings || frm.doc.deductions) { + frappe.call({ + method: "set_totals", + doc: frm.doc, + callback: function() { + frm.refresh_fields(); } - gross_pay += earning.amount; }); - frm.set_value('gross_pay', gross_pay); - - frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction); - frm.doc.rounded_total = Math.round(frm.doc.net_pay); - refresh_many(['net_pay', 'rounded_total']); - }); + } }; + +frappe.ui.form.on('Salary Detail', { + amount: function(frm) { + calculate_totals(frm); + }, + + earnings_remove: function(frm) { + calculate_totals(frm); + }, + + deductions_remove: function(frm) { + calculate_totals(frm); + }, + + salary_component: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.salary_component) { + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Salary Component", + name: child.salary_component + }, + callback: function(data) { + if (data.message) { + var result = data.message; + frappe.model.set_value(cdt, cdn, 'condition', result.condition); + frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); + if (result.amount_based_on_formula === 1) { + frappe.model.set_value(cdt, cdn, 'formula', result.formula); + } else { + frappe.model.set_value(cdt, cdn, 'amount', result.amount); + } + frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); + frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); + frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); + frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); + frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); + refresh_field("earnings"); + refresh_field("deductions"); + } + } + }); + } + }, + + amount_based_on_formula: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.amount_based_on_formula === 1) { + frappe.model.set_value(cdt, cdn, 'amount', null); + } else { + frappe.model.set_value(cdt, cdn, 'formula', null); + } + } +}); diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 619c45fa4a..386618cf08 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -18,6 +18,8 @@ "journal_entry", "payroll_entry", "company", + "currency", + "exchange_rate", "letter_head", "section_break_10", "start_date", @@ -38,6 +40,7 @@ "column_break_20", "total_working_hours", "hour_rate", + "base_hour_rate", "section_break_26", "bank_name", "bank_account_no", @@ -52,8 +55,10 @@ "deductions", "totals", "gross_pay", + "base_gross_pay", "column_break_25", "total_deduction", + "base_total_deduction", "loan_repayment", "loans", "section_break_43", @@ -63,10 +68,15 @@ "total_loan_repayment", "net_pay_info", "net_pay", + "base_net_pay", "column_break_53", "rounded_total", + "base_rounded_total", "section_break_55", "total_in_words", + "column_break_69", + "base_total_in_words", + "section_break_75", "amended_from" ], "fields": [ @@ -205,9 +215,13 @@ { "fieldname": "salary_structure", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Salary Structure", "options": "Salary Structure", - "read_only": 1 + "read_only": 1, + "reqd": 1, + "search_index": 1 }, { "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", @@ -265,7 +279,7 @@ "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency", + "options": "currency", "print_hide_if_no_value": 1 }, { @@ -347,24 +361,13 @@ "fieldname": "gross_pay", "fieldtype": "Currency", "label": "Gross Pay", - "oldfieldname": "gross_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { "fieldname": "column_break_25", "fieldtype": "Column Break" }, - { - "fieldname": "total_deduction", - "fieldtype": "Currency", - "label": "Total Deduction", - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "depends_on": "total_loan_repayment", "fieldname": "loan_repayment", @@ -379,6 +382,7 @@ "print_hide": 1 }, { + "depends_on": "eval:doc.docstatus != 0", "fieldname": "section_break_43", "fieldtype": "Section Break" }, @@ -416,13 +420,10 @@ "label": "net pay info" }, { - "description": "Gross Pay - Total Deduction - Loan Repayment", "fieldname": "net_pay", "fieldtype": "Currency", "label": "Net Pay", - "oldfieldname": "net_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -434,22 +435,13 @@ "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { "fieldname": "section_break_55", "fieldtype": "Section Break" }, - { - "description": "Net Pay (in words) will be visible once you save the Salary Slip.", - "fieldname": "total_in_words", - "fieldtype": "Data", - "label": "Total in words", - "oldfieldname": "net_pay_in_words", - "oldfieldtype": "Data", - "read_only": 1 - }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -500,13 +492,99 @@ { "fieldname": "column_break_18", "fieldtype": "Column Break" + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "total_in_words", + "fieldtype": "Data", + "label": "Total in words", + "length": 240, + "read_only": 1 + }, + { + "fieldname": "section_break_75", + "fieldtype": "Section Break" + }, + { + "fieldname": "base_hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate (Company Currency)", + "options": "Company:company:default_currency", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "base_gross_pay", + "fieldtype": "Currency", + "label": "Gross Pay (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "1.0", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Exchange Rate", + "print_hide": 1, + "reqd": 1 + }, + { + "fieldname": "base_total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "base_net_pay", + "fieldtype": "Currency", + "label": "Net Pay (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "bold": 1, + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "base_total_in_words", + "fieldtype": "Data", + "label": "Total in words (Company Currency)", + "length": 240, + "read_only": 1 + }, + { + "fieldname": "column_break_69", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-08-11 17:37:54.274384", + "modified": "2020-10-21 23:02:59.400249", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 7b87ae5e7b..20365b191d 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -50,16 +50,20 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() - company_currency = erpnext.get_company_currency(self.company) - total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total - self.total_in_words = money_in_words(total, company_currency) - if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet") if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)): frappe.msgprint(_("Total working hours should not be greater than max working hours {0}"). format(max_working_hours), alert=True) + def set_net_total_in_words(self): + doc_currency = self.currency + company_currency = erpnext.get_company_currency(self.company) + total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total + base_total = self.base_net_pay if self.is_rounding_total_disabled() else self.base_rounded_total + self.total_in_words = money_in_words(total, doc_currency) + self.base_total_in_words = money_in_words(base_total, company_currency) + def on_submit(self): if self.net_pay < 0: frappe.throw(_("Net Pay cannot be less than 0")) @@ -182,6 +186,7 @@ class SalarySlip(TransactionBase): if self.salary_slip_based_on_timesheet: self.salary_structure = self._salary_structure_doc.name self.hour_rate = self._salary_structure_doc.hour_rate + self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0 wages_amount = self.hour_rate * self.total_working_hours @@ -417,15 +422,22 @@ class SalarySlip(TransactionBase): if self.salary_structure: self.calculate_component_amounts("earnings") self.gross_pay = self.get_component_totals("earnings") + self.base_gross_pay = flt(flt(self.gross_pay) * flt(self.exchange_rate), self.precision('base_gross_pay')) if self.salary_structure: self.calculate_component_amounts("deductions") self.total_deduction = self.get_component_totals("deductions") + self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction')) self.set_loan_repayment() self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.rounded_total = rounded(self.net_pay) + self.base_net_pay = flt(flt(self.net_pay) * flt(self.exchange_rate), self.precision('base_net_pay')) + self.base_rounded_total = flt(rounded(self.base_net_pay), self.precision('base_net_pay')) + if self.hour_rate: + self.base_hour_rate = flt(flt(self.hour_rate) * flt(self.exchange_rate), self.precision('base_hour_rate')) + self.set_net_total_in_words() def calculate_component_amounts(self, component_type): if not getattr(self, '_salary_structure_doc', None): @@ -976,8 +988,9 @@ class SalarySlip(TransactionBase): amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") total_amount = amounts['interest_amount'] + amounts['payable_principal_amount'] if payment.total_payment > total_amount: - frappe.throw(_("Row {0}: Paid amount {1} is greater than pending accrued amount {2}against loan {3}").format( - payment.idx, frappe.bold(payment.total_payment),frappe.bold(total_amount), frappe.bold(payment.loan))) + frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2} against loan {3}""") + .format(payment.idx, frappe.bold(payment.total_payment), + frappe.bold(total_amount), frappe.bold(payment.loan))) self.total_interest_amount += payment.interest_amount self.total_principal_amount += payment.principal_amount @@ -1072,6 +1085,46 @@ class SalarySlip(TransactionBase): self.get_working_days_details(lwp=self.leave_without_pay) self.calculate_net_pay() + def set_totals(self): + self.gross_pay = 0 + if self.salary_slip_based_on_timesheet == 1: + self.calculate_total_for_salary_slip_based_on_timesheet() + else: + self.total_deduction = 0 + if self.earnings: + for earning in self.earnings: + self.gross_pay += flt(earning.amount) + if self.deductions: + for deduction in self.deductions: + self.total_deduction += flt(deduction.amount) + self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment) + self.set_base_totals() + + def set_base_totals(self): + self.base_gross_pay = flt(self.gross_pay) * flt(self.exchange_rate) + self.base_total_deduction = flt(self.total_deduction) * flt(self.exchange_rate) + self.rounded_total = rounded(self.net_pay) + self.base_net_pay = flt(self.net_pay) * flt(self.exchange_rate) + self.base_rounded_total = rounded(self.base_net_pay) + self.set_net_total_in_words() + + #calculate total working hours, earnings based on hourly wages and totals + def calculate_total_for_salary_slip_based_on_timesheet(self): + if self.timesheets: + for timesheet in self.timesheets: + if timesheet.working_hours: + self.total_working_hours += timesheet.working_hours + + wages_amount = self.total_working_hours * self.hour_rate + self.base_hour_rate = flt(self.hour_rate) * flt(self.exchange_rate) + salary_component = frappe.db.get_value('Salary Structure', {'name': self.salary_structure}, 'salary_component') + if self.earnings: + for i, earning in enumerate(self.earnings): + if earning.salary_component == salary_component: + self.earnings[i].amount = wages_amount + self.gross_pay += self.earnings[i].amount + self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) + def unlink_ref_doc_from_salary_slip(ref_no): linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip` where journal_entry=%s and docstatus < 2""", (ref_no)) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index e08dc7c9c8..71cb4083ed 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -33,7 +33,7 @@ class TestSalarySlip(unittest.TestCase): frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance") frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75) - emp_id = make_employee("test_for_attendance@salary.com") + emp_id = make_employee("test_payment_days_based_on_attendance@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) @@ -55,7 +55,7 @@ class TestSalarySlip(unittest.TestCase): mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp mark_attendance(emp_id, add_days(first_sunday, 7), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # invalid lwp - ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly") + ss = make_employee_salary_slip("test_payment_days_based_on_attendance@salary.com", "Monthly", "Test Payment Based On Attendence") self.assertEqual(ss.leave_without_pay, 1.25) self.assertEqual(ss.absent_days, 1) @@ -78,7 +78,7 @@ class TestSalarySlip(unittest.TestCase): # Payroll based on attendance frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") - emp_id = make_employee("test_for_attendance@salary.com") + emp_id = make_employee("test_payment_days_based_on_leave_application@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) @@ -108,7 +108,8 @@ class TestSalarySlip(unittest.TestCase): #two day leave ppl with fraction_of_daily_salary_per_leave = 0.5 equivalent to single day lwp make_leave_application(emp_id, add_days(first_sunday, 4), add_days(first_sunday, 5), "Test Partially Paid Leave") - ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly") + ss = make_employee_salary_slip("test_payment_days_based_on_leave_application@salary.com", "Monthly", "Test Payment Based On Leave Application") + self.assertEqual(ss.leave_without_pay, 4) @@ -127,12 +128,12 @@ class TestSalarySlip(unittest.TestCase): def test_salary_slip_with_holidays_included(self): no_of_days = self.get_no_of_days() frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) - make_employee("test_employee@salary.com") + make_employee("test_salary_slip_with_holidays_included@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + {"employee_name":"test_salary_slip_with_holidays_included@salary.com"}, "name"), "relieving_date", None) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") - ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") + {"employee_name":"test_salary_slip_with_holidays_included@salary.com"}, "name"), "status", "Active") + ss = make_employee_salary_slip("test_salary_slip_with_holidays_included@salary.com", "Monthly", "Test Salary Slip With Holidays Included") self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, no_of_days[0]) @@ -143,12 +144,12 @@ class TestSalarySlip(unittest.TestCase): def test_salary_slip_with_holidays_excluded(self): no_of_days = self.get_no_of_days() frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) - make_employee("test_employee@salary.com") + make_employee("test_salary_slip_with_holidays_excluded@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + {"employee_name":"test_salary_slip_with_holidays_excluded@salary.com"}, "name"), "relieving_date", None) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") - ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") + {"employee_name":"test_salary_slip_with_holidays_excluded@salary.com"}, "name"), "status", "Active") + ss = make_employee_salary_slip("test_salary_slip_with_holidays_excluded@salary.com", "Monthly", "Test Salary Slip With Holidays Excluded") self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1]) @@ -163,7 +164,7 @@ class TestSalarySlip(unittest.TestCase): frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) # set joinng date in the same month - make_employee("test_employee@salary.com") + make_employee("test_payment_days@salary.com") if getdate(nowdate()).day >= 15: relieving_date = getdate(add_days(nowdate(),-10)) date_of_joining = getdate(add_days(nowdate(),-10)) @@ -178,39 +179,39 @@ class TestSalarySlip(unittest.TestCase): relieving_date = getdate(nowdate()) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", date_of_joining) + {"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", date_of_joining) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + {"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", None) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + {"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Active") - ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") + ss = make_employee_salary_slip("test_payment_days@salary.com", "Monthly", "Test Payment Days") self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1)) # set relieving date in the same month frappe.db.set_value("Employee",frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) + {"employee_name":"test_payment_days@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60))) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date) + {"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", relieving_date) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left") + {"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Left") ss.save() self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.payment_days, getdate(relieving_date).day) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) + {"employee_name":"test_payment_days@salary.com"}, "name"), "relieving_date", None) frappe.db.set_value("Employee", frappe.get_value("Employee", - {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active") + {"employee_name":"test_payment_days@salary.com"}, "name"), "status", "Active") def test_employee_salary_slip_read_permission(self): - make_employee("test_employee@salary.com") + make_employee("test_employee_salary_slip_read_permission@salary.com") - salary_slip_test_employee = make_employee_salary_slip("test_employee@salary.com", "Monthly") - frappe.set_user("test_employee@salary.com") + salary_slip_test_employee = make_employee_salary_slip("test_employee_salary_slip_read_permission@salary.com", "Monthly", "Test Employee Salary Slip Read Permission") + frappe.set_user("test_employee_salary_slip_read_permission@salary.com") self.assertTrue(salary_slip_test_employee.has_permission("read")) def test_email_salary_slip(self): @@ -218,8 +219,8 @@ class TestSalarySlip(unittest.TestCase): frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 1) - make_employee("test_employee@salary.com") - ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") + make_employee("test_email_salary_slip@salary.com") + ss = make_employee_salary_slip("test_email_salary_slip@salary.com", "Monthly", "Test Salary Slip Email") ss.company = "_Test Company" ss.save() ss.submit() @@ -230,8 +231,9 @@ class TestSalarySlip(unittest.TestCase): def test_loan_repayment_salary_slip(self): from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - applicant = make_employee("test_loanemployee@salary.com", company="_Test Company") + applicant = make_employee("test_loan_repayment_salary_slip@salary.com", company="_Test Company") create_loan_accounts() @@ -243,6 +245,8 @@ class TestSalarySlip(unittest.TestCase): interest_income_account='Interest Income Account - _TC', penalty_income_account='Penalty Income Account - _TC') + make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR') + frappe.db.sql("""delete from `tabLoan""") loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1)) loan.repay_from_salary = 1 loan.submit() @@ -251,7 +255,7 @@ class TestSalarySlip(unittest.TestCase): process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) - ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly") + ss = make_employee_salary_slip("test_loan_repayment_salary_slip@salary.com", "Monthly", "Test Loan Repayment Salary Structure") ss.submit() self.assertEqual(ss.total_loan_repayment, 592) @@ -264,7 +268,7 @@ class TestSalarySlip(unittest.TestCase): for payroll_frequency in ["Monthly", "Bimonthly", "Fortnightly", "Weekly", "Daily"]: make_employee(payroll_frequency + "_test_employee@salary.com") - ss = make_employee_salary_slip(payroll_frequency + "_test_employee@salary.com", payroll_frequency) + ss = make_employee_salary_slip(payroll_frequency + "_test_employee@salary.com", payroll_frequency, payroll_frequency + "_Test Payroll Frequency") if payroll_frequency == "Monthly": self.assertEqual(ss.end_date, m['month_end_date']) elif payroll_frequency == "Bimonthly": @@ -279,6 +283,18 @@ class TestSalarySlip(unittest.TestCase): elif payroll_frequency == "Daily": self.assertEqual(ss.end_date, nowdate()) + def test_multi_currency_salary_slip(self): + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company") + frappe.db.sql("""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""") + salary_structure = make_salary_structure("Test Multi Currency Salary Slip", "Monthly", employee=applicant, company="_Test Company", currency='USD') + salary_slip = make_salary_slip(salary_structure.name, employee = applicant) + salary_slip.exchange_rate = 70 + salary_slip.calculate_net_pay() + + self.assertEqual(salary_slip.gross_pay, 78000) + self.assertEqual(salary_slip.base_gross_pay, 78000*70) + def test_tax_for_payroll_period(self): data = {} # test the impact of tax exemption declaration, tax exemption proof submission @@ -399,16 +415,21 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" employee = frappe.db.get_value("Employee", {"user_id": user}) - salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee) - salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}) + if not frappe.db.exists('Salary Structure', salary_structure): + salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee) + else: + salary_structure_doc = frappe.get_doc('Salary Structure', salary_structure) + salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}) - if not salary_slip: + if not salary_slip_name: salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee) salary_slip.employee_name = frappe.get_value("Employee", {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") salary_slip.payroll_frequency = payroll_frequency salary_slip.posting_date = nowdate() salary_slip.insert() + else: + salary_slip = frappe.get_doc('Salary Slip', salary_slip_name) return salary_slip @@ -449,7 +470,7 @@ def get_salary_component_account(sal_comp, company_list=None): sal_comp.append("accounts", { "company": d, - "default_account": create_account(account_name, d, parent_account) + "account": create_account(account_name, d, parent_account) }) sal_comp.save() @@ -576,7 +597,8 @@ def create_exemption_declaration(employee, payroll_period): "doctype": "Employee Tax Exemption Declaration", "employee": employee, "payroll_period": payroll_period, - "company": erpnext.get_default_company() + "company": erpnext.get_default_company(), + "currency": erpnext.get_default_currency() }) declaration.append("declarations", { "exemption_sub_category": "_Test Sub Category", @@ -591,7 +613,8 @@ def create_proof_submission(employee, payroll_period, amount): "doctype": "Employee Tax Exemption Proof Submission", "employee": employee, "payroll_period": payroll_period.name, - "submission_date": submission_date + "submission_date": submission_date, + "currency": erpnext.get_default_currency() }) proof_submission.append("tax_exemption_proofs", { "exemption_sub_category": "_Test Sub Category", @@ -608,13 +631,13 @@ def create_benefit_claim(employee, payroll_period, amount, component): "employee": employee, "claimed_amount": amount, "claim_date": claim_date, - "earning_component": component + "earning_component": component, + "currency": erpnext.get_default_currency() }).submit() return claim_date -def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False): - if frappe.db.exists("Income Tax Slab", "Tax Slab: " + payroll_period.name): - return +def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False, currency=erpnext.get_default_currency()): + frappe.db.sql("""delete from `tabIncome Tax Slab`""") slabs = [ { @@ -637,6 +660,7 @@ def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = income_tax_slab = frappe.new_doc("Income Tax Slab") income_tax_slab.name = "Tax Slab: " + payroll_period.name income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2) + income_tax_slab.currency = currency if allow_tax_exemption: income_tax_slab.allow_tax_exemption = 1 @@ -687,7 +711,8 @@ def create_additional_salary(employee, payroll_period, amount): "salary_component": "Performance Bonus", "payroll_date": salary_date, "amount": amount, - "type": "Earning" + "type": "Earning", + "currency": erpnext.get_default_currency() }).submit() return salary_date diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js index ad93a2fa4b..7daae49c58 100755 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -41,20 +41,6 @@ frappe.ui.form.on('Salary Structure', { frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) - frm.set_query("salary_component", "earnings", function() { - return { - filters: { - type: "earning" - } - } - }); - frm.set_query("salary_component", "deductions", function() { - return { - filters: { - type: "deduction" - } - } - }); frm.set_query("payment_account", function () { var account_types = ["Bank", "Cash"]; return { @@ -65,9 +51,48 @@ frappe.ui.form.on('Salary Structure', { } }; }); + frm.trigger('set_earning_deduction_component'); + }, + + set_earning_deduction_component: function(frm) { + if(!frm.doc.currency && !frm.doc.company) return; + frm.set_query("salary_component", "earnings", function() { + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + }; + }); + frm.set_query("salary_component", "deductions", function() { + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "deduction", currency: frm.doc.currency, company: frm.doc.company} + }; + }); + }, + + + currency: function(frm) { + calculate_totals(frm.doc); + frm.trigger("set_dynamic_labels") + frm.trigger('set_earning_deduction_component'); + frm.refresh() + }, + + set_dynamic_labels: function(frm) { + frm.set_currency_labels(["net_pay","hour_rate", "leave_encashment_amount_per_day", "max_benefits", "total_earning", + "total_deduction"], frm.doc.currency); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "earnings"); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "deductions"); + + frm.refresh_fields(); }, refresh: function(frm) { + frm.trigger("set_dynamic_labels") frm.trigger("toggle_fields"); frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); @@ -101,10 +126,12 @@ frappe.ui.form.on('Salary Structure', { fields: [ {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, {fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1}, + {fieldname: "currency", fieldtype: "Link", options: "Currency", label: __("Currency"), default: frm.doc.currency, read_only:1}, {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, - {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:"payroll_payable_account", fieldtype: "Link", options: "Account", filters: {"company": frm.doc.company, "root_type": "Liability", "is_group": 0, "account_currency": frm.doc.currency}, label: __("Payroll Payable Account")}, {fieldname:'base_variable', fieldtype:'Section Break'}, {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json index 5f94929f0b..de56fc8457 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.json +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json @@ -13,6 +13,7 @@ "column_break1", "is_active", "payroll_frequency", + "currency", "is_default", "time_sheet_earning_detail", "salary_slip_based_on_timesheet", @@ -26,9 +27,9 @@ "deductions", "conditions_and_formula_variable_and_example", "net_pay_detail", - "column_break2", "total_earning", "total_deduction", + "column_break2", "net_pay", "account", "mode_of_payment", @@ -43,23 +44,17 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", - "options": "Letter Head", - "show_days": 1, - "show_seconds": 1 + "options": "Letter Head" }, { "fieldname": "column_break1", "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -72,9 +67,7 @@ "oldfieldname": "is_active", "oldfieldtype": "Select", "options": "\nYes\nNo", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": "Monthly", @@ -82,9 +75,7 @@ "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", - "show_days": 1, - "show_seconds": 1 + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" }, { "default": "No", @@ -95,62 +86,46 @@ "no_copy": 1, "options": "Yes\nNo", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet", - "show_days": 1, - "show_seconds": 1 + "label": "Salary Slip Based on Timesheet" }, { "fieldname": "column_break_17", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "description": "Salary Component for timesheet based payroll.", "fieldname": "salary_component", "fieldtype": "Link", "label": "Salary Component", - "options": "Salary Component", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Component" }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "leave_encashment_amount_per_day", "fieldtype": "Currency", "label": "Leave Encashment Amount Per Day", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Amount)", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "description": "Salary breakup based on Earning and Deduction.", @@ -158,9 +133,7 @@ "fieldtype": "Section Break", "oldfieldname": "earning_deduction", "oldfieldtype": "Section Break", - "precision": "2", - "show_days": 1, - "show_seconds": 1 + "precision": "2" }, { "fieldname": "earnings", @@ -168,9 +141,7 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Detail" }, { "fieldname": "deductions", @@ -178,22 +149,16 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Detail" }, { "fieldname": "net_pay_detail", "fieldtype": "Section Break", - "options": "Simple", - "show_days": 1, - "show_seconds": 1 + "options": "Simple" }, { "fieldname": "column_break2", "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -201,63 +166,45 @@ "fieldtype": "Currency", "hidden": 1, "label": "Total Earning", - "oldfieldname": "total_earning", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "total_deduction", "fieldtype": "Currency", "hidden": 1, "label": "Total Deduction", - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "net_pay", "fieldtype": "Currency", "hidden": 1, "label": "Net Pay", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "account", "fieldtype": "Section Break", - "label": "Account", - "show_days": 1, - "show_seconds": 1 + "label": "Account" }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment", - "show_days": 1, - "show_seconds": 1 + "options": "Mode of Payment" }, { "fieldname": "column_break_28", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "payment_account", "fieldtype": "Link", "label": "Payment Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "amended_from", @@ -266,23 +213,26 @@ "no_copy": 1, "options": "Salary Structure", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "conditions_and_formula_variable_and_example", "fieldtype": "HTML", - "label": "Conditions and Formula variable and example", - "show_days": 1, - "show_seconds": 1 + "label": "Conditions and Formula variable and example" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-06-22 17:07:26.129355", + "modified": "2020-09-30 11:30:32.190798", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure", diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index ffc16d73c2..877e41d93c 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.utils import flt, cint, cstr from frappe import _ @@ -88,24 +88,26 @@ class SalaryStructure(Document): return employees @frappe.whitelist() - def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, - from_date=None, base=None, variable=None, income_tax_slab=None): - employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) + def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None): + employees = self.get_employees(company= self.company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, - employees=employees, salary_structure=self,from_date=from_date, - base=base, variable=variable, income_tax_slab=income_tax_slab) + employees=employees, salary_structure=self, + payroll_payable_account=payroll_payable_account, + from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab) else: - assign_salary_structure_for_employees(employees, self, from_date=from_date, - base=base, variable=variable, income_tax_slab=income_tax_slab) + assign_salary_structure_for_employees(employees, self, + payroll_payable_account=payroll_payable_account, + from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None): +def assign_salary_structure_for_employees(employees, salary_structure, payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None): salary_structures_assignments = [] existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 @@ -115,7 +117,7 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date count +=1 salary_structures_assignment = create_salary_structures_assignment(employee, - salary_structure, from_date, base, variable, income_tax_slab) + salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab) salary_structures_assignments.append(salary_structures_assignment) frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) @@ -123,11 +125,22 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date frappe.msgprint(_("Structures have been assigned successfully")) -def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None): +def create_salary_structures_assignment(employee, salary_structure, payroll_payable_account, from_date, base, variable, income_tax_slab=None): + if not payroll_payable_account: + payroll_payable_account = frappe.db.get_value('Company', salary_structure.company, 'default_payroll_payable_account') + if not payroll_payable_account: + frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults')) + payroll_payable_account_currency = frappe.db.get_value('Account', payroll_payable_account, 'account_currency') + company_curency = erpnext.get_company_currency(salary_structure.company) + if payroll_payable_account_currency != salary_structure.currency and payroll_payable_account_currency != company_curency: + frappe.throw(_("Invalid Payroll Payable Account. The account currency must be {0} or {1}").format(salary_structure.currency, company_curency)) + assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name assignment.company = salary_structure.company + assignment.currency = salary_structure.currency + assignment.payroll_payable_account = payroll_payable_account assignment.from_date = from_date assignment.base = base assignment.variable = variable @@ -170,7 +183,8 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = "doctype": "Salary Slip", "field_map": { "total_earning": "gross_pay", - "name": "salary_structure" + "name": "salary_structure", + "currency": "currency" } } }, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions) @@ -188,7 +202,22 @@ def get_employees(salary_structure): filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee']) if not employees: - frappe.throw(_("There's no Employee with Salary Structure: {0}. \ - Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure)) + frappe.throw(_("There's no Employee with Salary Structure: {0}. Assign {1} to an Employee to preview Salary Slip").format( + salary_structure, salary_structure)) return list(set([d.employee for d in employees])) + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters): + if len(filters) < 3: + return {} + + return frappe.db.sql(""" + select t1.salary_component + from `tabSalary Component` t1, `tabSalary Component Account` t2 + where t1.salary_component = t2.parent + and t1.type = %s + and t2.company = %s + order by salary_component + """, (filters['type'], filters['company']) ) diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py index e04fda8120..abb669740b 100644 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -94,7 +94,8 @@ class TestSalaryStructure(unittest.TestCase): self.assertFalse(("\n" in row.formula) or ("\n" in row.condition)) def test_salary_structures_assignment(self): - salary_structure = make_salary_structure("Salary Structure Sample", "Monthly") + company_currency = erpnext.get_default_currency() + salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", currency=company_currency) employee = "test_assign_stucture@salary.com" employee_doc_name = make_employee(employee) # clear the already assigned stuctures @@ -107,8 +108,13 @@ class TestSalaryStructure(unittest.TestCase): self.assertEqual(salary_structure_assignment.base, 5000) self.assertEqual(salary_structure_assignment.variable, 200) + def test_multi_currency_salary_structure(self): + make_employee("test_muti_currency_employee@salary.com") + sal_struct = make_salary_structure("Salary Structure Multi Currency", "Monthly", currency='USD') + self.assertEqual(sal_struct.currency, 'USD') + def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, - test_tax=False, company=None): + test_tax=False, company=None, currency=erpnext.get_default_currency()): if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure)) @@ -120,7 +126,8 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do "earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "payroll_frequency": payroll_frequency, - "payment_account": get_random("Account") + "payment_account": get_random("Account", filters={'account_currency': currency}), + "currency": currency } if other_details and isinstance(other_details, dict): details.update(other_details) @@ -134,16 +141,16 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do if employee and not frappe.db.get_value("Salary Structure Assignment", {'employee':employee, 'docstatus': 1}) and salary_structure_doc.docstatus==1: - create_salary_structure_assignment(employee, salary_structure, company=company) + create_salary_structure_assignment(employee, salary_structure, company=company, currency=currency) return salary_structure_doc -def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None): +def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None, currency=erpnext.get_default_currency()): if frappe.db.exists("Salary Structure Assignment", {"employee": employee}): frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee)) payroll_period = create_payroll_period() - create_tax_slab(payroll_period, allow_tax_exemption=True) + create_tax_slab(payroll_period, allow_tax_exemption=True, currency=currency) salary_structure_assignment = frappe.new_doc("Salary Structure Assignment") salary_structure_assignment.employee = employee @@ -151,8 +158,15 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non salary_structure_assignment.variable = 5000 salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1) salary_structure_assignment.salary_structure = salary_structure + salary_structure_assignment.currency = currency + salary_structure_assignment.payroll_payable_account = get_payable_account(company) salary_structure_assignment.company = company or erpnext.get_default_company() salary_structure_assignment.save(ignore_permissions=True) salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period" salary_structure_assignment.submit() return salary_structure_assignment + +def get_payable_account(company=None): + if not company: + company = erpnext.get_default_company() + return frappe.db.get_value("Company", company, "default_payroll_payable_account") \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js index 818e853154..6cd897e95d 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -6,9 +6,6 @@ frappe.ui.form.on('Salary Structure Assignment', { frm.set_query("employee", function() { return { query: "erpnext.controllers.queries.employee_query", - filters: { - company: frm.doc.company - } } }); frm.set_query("salary_structure", function() { @@ -26,11 +23,25 @@ frappe.ui.form.on('Salary Structure Assignment', { filters: { company: frm.doc.company, docstatus: 1, - disabled: 0 + disabled: 0, + currency: frm.doc.currency + } + }; + }); + + frm.set_query("payroll_payable_account", function() { + var company_currency = erpnext.get_currency(frm.doc.company); + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + "account_currency": ["in", [frm.doc.currency, company_currency]], } } }); }, + employee: function(frm) { if(frm.doc.employee){ frappe.call({ @@ -52,5 +63,13 @@ frappe.ui.form.on('Salary Structure Assignment', { else{ frm.set_value("company", null); } + }, + + company: function(frm) { + if (frm.doc.company) { + frappe.db.get_value("Company", frm.doc.company, "default_payroll_payable_account", (r) => { + frm.set_value("payroll_payable_account", r.default_payroll_payable_account); + }); + } } }); diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index c84e034c72..92bb347661 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -11,11 +11,13 @@ "employee_name", "department", "company", + "payroll_payable_account", "column_break_6", "designation", "salary_structure", "from_date", "income_tax_slab", + "currency", "section_break_7", "base", "column_break_9", @@ -94,7 +96,7 @@ "fieldname": "base", "fieldtype": "Currency", "label": "Base", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "column_break_9", @@ -104,7 +106,7 @@ "fieldname": "variable", "fieldtype": "Currency", "label": "Variable", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "amended_from", @@ -116,15 +118,35 @@ "read_only": 1 }, { + "depends_on": "salary_structure", "fieldname": "income_tax_slab", "fieldtype": "Link", "label": "Income Tax Slab", "options": "Income Tax Slab" + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "employee", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "options": "Account" } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 19:58:09.964692", + "modified": "2020-11-30 18:07:48.251311", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure Assignment", diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py index 668e0ec471..dccb5df1a1 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -13,6 +13,8 @@ class DuplicateAssignment(frappe.ValidationError): pass class SalaryStructureAssignment(Document): def validate(self): self.validate_dates() + self.validate_income_tax_slab() + self.set_payroll_payable_account() def validate_dates(self): joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, @@ -31,6 +33,24 @@ class SalaryStructureAssignment(Document): frappe.throw(_("From Date {0} cannot be after employee's relieving Date {1}") .format(self.from_date, relieving_date)) + def validate_income_tax_slab(self): + if not self.income_tax_slab: + return + + income_tax_slab_currency = frappe.db.get_value('Income Tax Slab', self.income_tax_slab, 'currency') + if self.currency != income_tax_slab_currency: + frappe.throw(_("Currency of selected Income Tax Slab should be {0} instead of {1}").format(self.currency, income_tax_slab_currency)) + + def set_payroll_payable_account(self): + if not self.payroll_payable_account: + payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payable_account') + if not payroll_payable_account: + payroll_payable_account = frappe.db.get_value( + "Account", { + "account_name": _("Payroll Payable"), "company": self.company, "account_currency": frappe.db.get_value( + "Company", self.company, "default_currency"), "is_group": 0}) + self.payroll_payable_account = payroll_payable_account + def get_assigned_salary_structure(employee, on_date): if not employee or not on_date: return None @@ -43,3 +63,10 @@ def get_assigned_salary_structure(employee, on_date): 'on_date': on_date, }) return salary_structure[0][0] if salary_structure else None + +@frappe.whitelist() +def get_employee_currency(employee): + employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency') + if not employee_currency: + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(employee)) + return employee_currency \ No newline at end of file diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json index 94eda4c043..65d3824f3a 100644 --- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -19,13 +19,15 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "From Amount", + "options": "currency", "reqd": 1 }, { "fieldname": "to_amount", "fieldtype": "Currency", "in_list_view": 1, - "label": "To Amount" + "label": "To Amount", + "options": "currency" }, { "default": "0", @@ -53,7 +55,7 @@ ], "istable": 1, "links": [], - "modified": "2020-06-22 18:16:07.596493", + "modified": "2020-10-19 13:44:39.549337", "modified_by": "Administrator", "module": "Payroll", "name": "Taxable Salary Slab", diff --git a/erpnext/payroll/report/salary_register/salary_register.js b/erpnext/payroll/report/salary_register/salary_register.js index 885e3d13c7..eb4acb91a7 100644 --- a/erpnext/payroll/report/salary_register/salary_register.js +++ b/erpnext/payroll/report/salary_register/salary_register.js @@ -8,34 +8,48 @@ frappe.query_reports["Salary Register"] = { "label": __("From"), "fieldtype": "Date", "default": frappe.datetime.add_months(frappe.datetime.get_today(),-1), - "reqd": 1 + "reqd": 1, + "width": "100px" }, { "fieldname":"to_date", "label": __("To"), "fieldtype": "Date", "default": frappe.datetime.get_today(), - "reqd": 1 + "reqd": 1, + "width": "100px" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "options": "Currency", + "label": __("Currency"), + "default": erpnext.get_currency(frappe.defaults.get_default("Company")), + "width": "50px" }, { "fieldname":"employee", "label": __("Employee"), "fieldtype": "Link", - "options": "Employee" + "options": "Employee", + "width": "100px" }, { "fieldname":"company", "label": __("Company"), "fieldtype": "Link", "options": "Company", - "default": frappe.defaults.get_user_default("Company") + "default": frappe.defaults.get_user_default("Company"), + "width": "100px", + "reqd": 1 }, { "fieldname":"docstatus", "label":__("Document Status"), "fieldtype":"Select", "options":["Draft", "Submitted", "Cancelled"], - "default":"Submitted" + "default": "Submitted", + "width": "100px" } ] } diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py index 87010855fd..a1b1a8c56b 100644 --- a/erpnext/payroll/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -2,18 +2,22 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.utils import flt from frappe import _ def execute(filters=None): if not filters: filters = {} - salary_slips = get_salary_slips(filters) + currency = None + if filters.get('currency'): + currency = filters.get('currency') + company_currency = erpnext.get_company_currency(filters.get("company")) + salary_slips = get_salary_slips(filters, company_currency) if not salary_slips: return [], [] columns, earning_types, ded_types = get_columns(salary_slips) - ss_earning_map = get_ss_earning_map(salary_slips) - ss_ded_map = get_ss_ded_map(salary_slips) + ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency) + ss_ded_map = get_ss_ded_map(salary_slips,currency, company_currency) doj_map = get_employee_doj_map() data = [] @@ -21,24 +25,30 @@ def execute(filters=None): row = [ss.name, ss.employee, ss.employee_name, doj_map.get(ss.employee), ss.branch, ss.department, ss.designation, ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days] - if not ss.branch == None:columns[3] = columns[3].replace('-1','120') - if not ss.department == None: columns[4] = columns[4].replace('-1','120') - if not ss.designation == None: columns[5] = columns[5].replace('-1','120') - if not ss.leave_without_pay == None: columns[9] = columns[9].replace('-1','130') + if ss.branch is not None: columns[3] = columns[3].replace('-1','120') + if ss.department is not None: columns[4] = columns[4].replace('-1','120') + if ss.designation is not None: columns[5] = columns[5].replace('-1','120') + if ss.leave_without_pay is not None: columns[9] = columns[9].replace('-1','130') for e in earning_types: row.append(ss_earning_map.get(ss.name, {}).get(e)) - row += [ss.gross_pay] + if currency == company_currency: + row += [flt(ss.gross_pay) * flt(ss.exchange_rate)] + else: + row += [ss.gross_pay] for d in ded_types: row.append(ss_ded_map.get(ss.name, {}).get(d)) row.append(ss.total_loan_repayment) - row += [ss.total_deduction, ss.net_pay] - + if currency == company_currency: + row += [flt(ss.total_deduction) * flt(ss.exchange_rate), flt(ss.net_pay) * flt(ss.exchange_rate)] + else: + row += [ss.total_deduction, ss.net_pay] + row.append(currency or company_currency) data.append(row) return columns, data @@ -46,10 +56,19 @@ def execute(filters=None): def get_columns(salary_slips): """ columns = [ - _("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", - _("Date of Joining") + "::80", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80", - _("End Date") + "::80", _("Leave Without Pay") + ":Float:130", _("Payment Days") + ":Float:120" + _("Salary Slip ID") + ":Link/Salary Slip:150", + _("Employee") + ":Link/Employee:120", + _("Employee Name") + "::140", + _("Date of Joining") + "::80", + _("Branch") + ":Link/Branch:120", + _("Department") + ":Link/Department:120", + _("Designation") + ":Link/Designation:120", + _("Company") + ":Link/Company:120", + _("Start Date") + "::80", + _("End Date") + "::80", + _("Leave Without Pay") + ":Float:130", + _("Payment Days") + ":Float:120", + _("Currency") + ":Link/Currency:80" ] """ columns = [ @@ -73,15 +92,15 @@ def get_columns(salary_slips): return columns, salary_components[_("Earning")], salary_components[_("Deduction")] -def get_salary_slips(filters): +def get_salary_slips(filters, company_currency): filters.update({"from_date": filters.get("from_date"), "to_date":filters.get("to_date")}) - conditions, filters = get_conditions(filters) + conditions, filters = get_conditions(filters, company_currency) salary_slips = frappe.db.sql("""select * from `tabSalary Slip` where %s order by employee""" % conditions, filters, as_dict=1) return salary_slips or [] -def get_conditions(filters): +def get_conditions(filters, company_currency): conditions = "" doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} @@ -92,6 +111,8 @@ def get_conditions(filters): if filters.get("to_date"): conditions += " and end_date <= %(to_date)s" if filters.get("company"): conditions += " and company = %(company)s" if filters.get("employee"): conditions += " and employee = %(employee)s" + if filters.get("currency") and filters.get("currency") != company_currency: + conditions += " and currency = %(currency)s" return conditions, filters @@ -103,26 +124,32 @@ def get_employee_doj_map(): FROM `tabEmployee` """)) -def get_ss_earning_map(salary_slips): - ss_earnings = frappe.db.sql("""select parent, salary_component, amount - from `tabSalary Detail` where parent in (%s)""" % +def get_ss_earning_map(salary_slips, currency, company_currency): + ss_earnings = frappe.db.sql("""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name + from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) ss_earning_map = {} for d in ss_earnings: ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) - ss_earning_map[d.parent][d.salary_component] = flt(d.amount) + if currency == company_currency: + ss_earning_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) + else: + ss_earning_map[d.parent][d.salary_component] = flt(d.amount) return ss_earning_map -def get_ss_ded_map(salary_slips): - ss_deductions = frappe.db.sql("""select parent, salary_component, amount - from `tabSalary Detail` where parent in (%s)""" % +def get_ss_ded_map(salary_slips, currency, company_currency): + ss_deductions = frappe.db.sql("""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name + from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" % (', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1) ss_ded_map = {} for d in ss_deductions: ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) - ss_ded_map[d.parent][d.salary_component] = flt(d.amount) + if currency == company_currency: + ss_ded_map[d.parent][d.salary_component] = flt(d.amount) * flt(d.exchange_rate if d.exchange_rate else 1) + else: + ss_ded_map[d.parent][d.salary_component] = flt(d.amount) return ss_ded_map From ad57eef40c1dc6710218387a9d1f5c8ead43c7f3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 1 Dec 2020 09:14:57 +0530 Subject: [PATCH 195/283] fix(product-listing): Check if customer exists (#24030) - It might happen that perty_name might not always be Customer (it might be Supplier as well) --- erpnext/shopping_cart/cart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 0ccc0252c3..c2549fe7dd 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -345,7 +345,7 @@ def _set_price_list(cart_settings, quotation=None): selling_price_list = None # check if default customer price list exists - if party_name: + if party_name and frappe.db.exists("Customer", party_name): selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) # check default price list in shopping cart From 029b9c08ddf6c4b2b77b3c8f221e16bc3af76fa0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 1 Dec 2020 09:33:17 +0530 Subject: [PATCH 196/283] Update bom.py --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c6699200dc..6363242b0a 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -169,7 +169,7 @@ class BOM(WebsiteGenerator): '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' : flt(rate) * (flt(self.conversion_rate) or 1), - 'include_item_in_manufacturing': cint(args['transfer_for_manufacture'], 0), + 'include_item_in_manufacturing': cint(args.get('transfer_for_manufacture')), 'sourced_by_supplier' : args.get('sourced_by_supplier', 0) } From de68f74b4c00fec82bb0ff3a7970e82616002004 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 1 Dec 2020 12:51:38 +0530 Subject: [PATCH 197/283] chore: update all desk pages --- .../desk_page/accounting/accounting.json | 95 ++++++++++++++++++- .../desk_page/agriculture/agriculture.json | 13 ++- erpnext/assets/desk_page/assets/assets.json | 14 ++- erpnext/buying/desk_page/buying/buying.json | 41 +++++++- erpnext/crm/desk_page/crm/crm.json | 33 ++++++- .../desk_page/education/education.json | 54 ++++++++++- .../erpnext_integrations.json | 9 +- .../erpnext_integrations_settings.json | 7 +- .../desk_page/healthcare/healthcare.json | 42 +++++++- erpnext/hr/desk_page/hr/hr.json | 79 ++++++++++++++- .../loan_management/desk_page/loan/loan.json | 19 +++- .../manufacturing/manufacturing.json | 24 ++++- .../desk_page/non_profit/non_profit.json | 15 ++- .../payroll/desk_page/payroll/payroll.json | 26 ++++- .../projects/desk_page/projects/projects.json | 13 ++- .../desk_page/quality/quality.json | 11 ++- erpnext/selling/desk_page/retail/retail.json | 8 +- .../selling/desk_page/selling/selling.json | 48 +++++++++- .../erpnext_settings/erpnext_settings.json | 2 +- erpnext/setup/desk_page/home/home.json | 36 ++++++- erpnext/stock/desk_page/stock/stock.json | 60 +++++++++++- .../support/desk_page/support/support.json | 12 ++- .../desk_page/utilities/utilities.json | 4 +- 23 files changed, 642 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 2de2492d2a..ccc51685db 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -108,6 +108,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Company", @@ -117,6 +118,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts", @@ -126,6 +128,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Accounts Settings", @@ -135,6 +138,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fiscal Year", @@ -144,6 +148,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Accounting Dimension", @@ -153,6 +158,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Finance Book", @@ -162,6 +168,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Accounting Period", @@ -171,6 +178,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payment Term", @@ -187,6 +195,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Journal Entry", @@ -196,6 +205,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Journal Entry Template", @@ -205,6 +215,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "General Ledger", @@ -214,6 +225,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Customer Ledger Summary", @@ -223,6 +235,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Supplier Ledger Summary", @@ -239,6 +252,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Invoice", @@ -248,6 +262,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer", @@ -257,6 +272,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payment Entry", @@ -266,6 +282,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payment Request", @@ -275,6 +292,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Accounts Receivable", @@ -284,6 +302,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Accounts Receivable Summary", @@ -293,6 +312,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Register", @@ -302,6 +322,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Item-wise Sales Register", @@ -311,6 +332,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", @@ -320,6 +342,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Delivered Items To Be Billed", @@ -336,6 +359,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Purchase Invoice", @@ -345,6 +369,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier", @@ -354,6 +379,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payment Entry", @@ -363,6 +389,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Accounts Payable", @@ -372,6 +399,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Accounts Payable Summary", @@ -381,6 +409,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Purchase Register", @@ -390,6 +419,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Item-wise Purchase Register", @@ -399,6 +429,7 @@ "type": "Link" }, { + "dependencies": "Purchase Order", "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", @@ -408,6 +439,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Received Items To Be Billed", @@ -424,6 +456,7 @@ "type": "Card Break" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Trial Balance for Party", @@ -433,6 +466,7 @@ "type": "Link" }, { + "dependencies": "Journal Entry", "hidden": 0, "is_query_report": 1, "label": "Payment Period Based On Invoice Date", @@ -442,6 +476,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Partners Commission", @@ -451,6 +486,7 @@ "type": "Link" }, { + "dependencies": "Customer", "hidden": 0, "is_query_report": 1, "label": "Customer Credit Balance", @@ -460,6 +496,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Payment Summary", @@ -469,6 +506,7 @@ "type": "Link" }, { + "dependencies": "Address", "hidden": 0, "is_query_report": 1, "label": "Address And Contacts", @@ -478,6 +516,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "DATEV Export", @@ -494,6 +533,7 @@ "type": "Card Break" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Trial Balance", @@ -503,6 +543,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Profit and Loss Statement", @@ -512,6 +553,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Balance Sheet", @@ -521,6 +563,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Cash Flow", @@ -530,6 +573,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Consolidated Financial Statement", @@ -546,6 +590,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Currency", @@ -555,6 +600,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Currency Exchange", @@ -564,6 +610,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Exchange Rate Revaluation", @@ -580,6 +627,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payment Gateway Account", @@ -589,6 +637,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", @@ -598,6 +647,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Mode of Payment", @@ -614,6 +664,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank", @@ -623,6 +674,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank Account", @@ -632,6 +684,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank Clearance", @@ -641,6 +694,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank Reconciliation", @@ -650,6 +704,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Bank Reconciliation Statement", @@ -659,6 +714,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank Statement Transaction Entry", @@ -668,6 +724,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Bank Statement Settings", @@ -684,6 +741,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Subscription Plan", @@ -693,6 +751,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Subscription", @@ -702,6 +761,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Subscription Settings", @@ -718,6 +778,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "GST Settings", @@ -727,6 +788,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "GST HSN Code", @@ -736,6 +798,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GSTR-1", @@ -745,6 +808,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GSTR-2", @@ -754,6 +818,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "GSTR 3B Report", @@ -763,6 +828,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GST Sales Register", @@ -772,6 +838,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GST Purchase Register", @@ -781,6 +848,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GST Itemised Sales Register", @@ -790,6 +858,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "GST Itemised Purchase Register", @@ -799,6 +868,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "C-Form", @@ -808,6 +878,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lower Deduction Certificate", @@ -824,6 +895,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shareholder", @@ -833,6 +905,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Share Transfer", @@ -842,6 +915,7 @@ "type": "Link" }, { + "dependencies": "Share Transfer", "hidden": 0, "is_query_report": 1, "label": "Share Ledger", @@ -851,6 +925,7 @@ "type": "Link" }, { + "dependencies": "Share Transfer", "hidden": 0, "is_query_report": 1, "label": "Share Balance", @@ -867,6 +942,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chart of Cost Centers", @@ -876,6 +952,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Budget", @@ -885,6 +962,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Accounting Dimension", @@ -894,6 +972,7 @@ "type": "Link" }, { + "dependencies": "Cost Center", "hidden": 0, "is_query_report": 1, "label": "Budget Variance Report", @@ -903,6 +982,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Monthly Distribution", @@ -919,6 +999,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Opening Invoice Creation Tool", @@ -928,6 +1009,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts Importer", @@ -937,6 +1019,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Period Closing Voucher", @@ -953,6 +1036,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Taxes and Charges Template", @@ -962,6 +1046,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Purchase Taxes and Charges Template", @@ -971,6 +1056,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Tax Template", @@ -980,6 +1066,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Tax Category", @@ -989,6 +1076,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Tax Rule", @@ -998,6 +1086,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Tax Withholding Category", @@ -1014,6 +1103,7 @@ "type": "Card Break" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Gross Profit", @@ -1023,6 +1113,7 @@ "type": "Link" }, { + "dependencies": "GL Entry", "hidden": 0, "is_query_report": 1, "label": "Profitability Analysis", @@ -1032,6 +1123,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Invoice Trends", @@ -1041,6 +1133,7 @@ "type": "Link" }, { + "dependencies": "Purchase Invoice", "hidden": 0, "is_query_report": 1, "label": "Purchase Invoice Trends", @@ -1050,7 +1143,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:37.583879", + "modified": "2020-12-01 12:34:03.892648", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 145ef07c10..38da1c43e8 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -38,6 +38,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Crop", @@ -47,6 +48,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Crop Cycle", @@ -56,6 +58,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Location", @@ -72,6 +75,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Plant Analysis", @@ -81,6 +85,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Soil Analysis", @@ -90,6 +95,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Water Analysis", @@ -99,6 +105,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Soil Texture", @@ -108,6 +115,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Weather", @@ -117,6 +125,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Agriculture Analysis Criteria", @@ -133,6 +142,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Disease", @@ -142,6 +152,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fertilizer", @@ -151,7 +162,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:42.501139", + "modified": "2020-12-01 12:33:59.698335", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 66f448fc9c..54572156d7 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -43,6 +43,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Asset", @@ -52,6 +53,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Location", @@ -61,6 +63,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Asset Category", @@ -70,6 +73,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Asset Movement", @@ -86,6 +90,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance Team", @@ -95,6 +100,7 @@ "type": "Link" }, { + "dependencies": "Asset Maintenance Team", "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance", @@ -104,6 +110,7 @@ "type": "Link" }, { + "dependencies": "Asset Maintenance", "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance Log", @@ -113,6 +120,7 @@ "type": "Link" }, { + "dependencies": "Asset", "hidden": 0, "is_query_report": 0, "label": "Asset Value Adjustment", @@ -122,6 +130,7 @@ "type": "Link" }, { + "dependencies": "Asset", "hidden": 0, "is_query_report": 0, "label": "Asset Repair", @@ -138,6 +147,7 @@ "type": "Card Break" }, { + "dependencies": "Asset", "hidden": 0, "is_query_report": 1, "label": "Asset Depreciation Ledger", @@ -147,6 +157,7 @@ "type": "Link" }, { + "dependencies": "Asset", "hidden": 0, "is_query_report": 1, "label": "Asset Depreciations and Balances", @@ -156,6 +167,7 @@ "type": "Link" }, { + "dependencies": "Asset Maintenance", "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance", @@ -165,7 +177,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:41.714472", + "modified": "2020-12-01 12:34:00.417242", "modified_by": "Administrator", "module": "Assets", "name": "Assets", diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 5b85e6ab10..e25554c796 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -70,6 +70,7 @@ "type": "Card Break" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Material Request", @@ -79,6 +80,7 @@ "type": "Link" }, { + "dependencies": "Item, Supplier", "hidden": 0, "is_query_report": 0, "label": "Purchase Order", @@ -88,6 +90,7 @@ "type": "Link" }, { + "dependencies": "Item, Supplier", "hidden": 0, "is_query_report": 0, "label": "Purchase Invoice", @@ -97,6 +100,7 @@ "type": "Link" }, { + "dependencies": "Item, Supplier", "hidden": 0, "is_query_report": 0, "label": "Request for Quotation", @@ -106,6 +110,7 @@ "type": "Link" }, { + "dependencies": "Item, Supplier", "hidden": 0, "is_query_report": 0, "label": "Supplier Quotation", @@ -122,6 +127,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item", @@ -131,6 +137,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Price", @@ -140,6 +147,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Price List", @@ -149,6 +157,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Product Bundle", @@ -158,6 +167,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Group", @@ -167,6 +177,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Promotional Scheme", @@ -176,6 +187,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", @@ -192,6 +204,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Buying Settings", @@ -201,6 +214,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Purchase Taxes and Charges Template", @@ -210,6 +224,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", @@ -226,6 +241,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier", @@ -235,6 +251,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier Group", @@ -244,6 +261,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Contact", @@ -253,6 +271,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Address", @@ -269,6 +288,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard", @@ -278,6 +298,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Variable", @@ -287,6 +308,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Criteria", @@ -296,6 +318,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Standing", @@ -312,6 +335,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Purchase Analytics", @@ -321,6 +345,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", @@ -330,6 +355,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Supplier-Wise Sales Analytics", @@ -339,6 +365,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Items to Order and Receive", @@ -348,6 +375,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Purchase Order Trends", @@ -357,6 +385,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Procurement Tracker", @@ -373,6 +402,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Items To Be Requested", @@ -382,6 +412,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Item-wise Purchase History", @@ -391,6 +422,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Purchase Receipt Trends", @@ -400,6 +432,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Purchase Invoice Trends", @@ -409,6 +442,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Subcontracted Raw Materials To Be Transferred", @@ -418,6 +452,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Subcontracted Item To Be Received", @@ -427,6 +462,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Supplier Quotation Comparison", @@ -436,6 +472,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Material Requests for which Supplier Quotations are not created", @@ -445,6 +482,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Supplier Addresses And Contacts", @@ -461,6 +499,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Import Supplier Invoice", @@ -470,7 +509,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:43.304970", + "modified": "2020-12-01 12:33:59.349265", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index fd97ba6c0c..8679065bca 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -52,6 +52,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lead", @@ -61,6 +62,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Opportunity", @@ -70,6 +72,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer", @@ -79,6 +82,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Contact", @@ -88,6 +92,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Communication", @@ -97,6 +102,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lead Source", @@ -106,6 +112,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Contract", @@ -115,6 +122,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Appointment", @@ -124,6 +132,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Newsletter", @@ -140,6 +149,7 @@ "type": "Card Break" }, { + "dependencies": "Lead", "hidden": 0, "is_query_report": 1, "label": "Lead Details", @@ -149,6 +159,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Funnel", @@ -158,6 +169,7 @@ "type": "Link" }, { + "dependencies": "Lead", "hidden": 0, "is_query_report": 1, "label": "Prospects Engaged But Not Converted", @@ -167,6 +179,7 @@ "type": "Link" }, { + "dependencies": "Opportunity", "hidden": 0, "is_query_report": 1, "label": "First Response Time for Opportunity", @@ -176,6 +189,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Inactive Customers", @@ -185,6 +199,7 @@ "type": "Link" }, { + "dependencies": "Lead", "hidden": 0, "is_query_report": 1, "label": "Campaign Efficiency", @@ -194,6 +209,7 @@ "type": "Link" }, { + "dependencies": "Lead", "hidden": 0, "is_query_report": 1, "label": "Lead Owner Efficiency", @@ -210,6 +226,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Maintenance Schedule", @@ -219,6 +236,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Maintenance Visit", @@ -228,6 +246,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Warranty Claim", @@ -244,6 +263,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Campaign", @@ -253,6 +273,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Email Campaign", @@ -262,6 +283,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Social Media Post", @@ -278,6 +300,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer Group", @@ -287,6 +310,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Territory", @@ -296,6 +320,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Person", @@ -305,6 +330,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "SMS Center", @@ -314,6 +340,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "SMS Log", @@ -323,6 +350,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "SMS Settings", @@ -332,6 +360,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Email Group", @@ -341,6 +370,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Twitter Settings", @@ -350,6 +380,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "LinkedIn Settings", @@ -359,7 +390,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:40.216301", + "modified": "2020-12-01 12:34:01.909417", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index 06d7797933..a4bdb21ae6 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -93,6 +93,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student", @@ -102,6 +103,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Instructor", @@ -111,6 +113,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Guardian", @@ -120,6 +123,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Group", @@ -129,6 +133,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Log", @@ -145,6 +150,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Program", @@ -154,6 +160,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course", @@ -163,6 +170,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Topic", @@ -172,6 +180,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Room", @@ -188,6 +197,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Article", @@ -197,6 +207,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Video", @@ -206,6 +217,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quiz", @@ -222,6 +234,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Education Settings", @@ -231,6 +244,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Category", @@ -240,6 +254,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Batch Name", @@ -249,6 +264,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Grading Scale", @@ -258,6 +274,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Academic Term", @@ -267,6 +284,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Academic Year", @@ -283,6 +301,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Applicant", @@ -292,6 +311,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Admission", @@ -301,6 +321,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Program Enrollment", @@ -310,6 +331,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Enrollment", @@ -326,6 +348,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fee Structure", @@ -335,6 +358,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fee Category", @@ -344,6 +368,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fee Schedule", @@ -353,6 +378,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fees", @@ -362,6 +388,7 @@ "type": "Link" }, { + "dependencies": "Fees", "hidden": 0, "is_query_report": 1, "label": "Student Fee Collection Report", @@ -371,6 +398,7 @@ "type": "Link" }, { + "dependencies": "Fees", "hidden": 0, "is_query_report": 1, "label": "Program wise Fee Collection Report", @@ -387,6 +415,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Schedule", @@ -396,6 +425,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Scheduling Tool", @@ -412,6 +442,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Attendance", @@ -421,6 +452,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Leave Application", @@ -430,6 +462,7 @@ "type": "Link" }, { + "dependencies": "Student Attendance", "hidden": 0, "is_query_report": 1, "label": "Student Monthly Attendance Sheet", @@ -439,6 +472,7 @@ "type": "Link" }, { + "dependencies": "Student Attendance", "hidden": 0, "is_query_report": 1, "label": "Absent Student Report", @@ -448,6 +482,7 @@ "type": "Link" }, { + "dependencies": "Student Attendance", "hidden": 0, "is_query_report": 1, "label": "Student Batch-Wise Attendance", @@ -464,6 +499,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Enrollment", @@ -473,6 +509,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Activity", @@ -482,6 +519,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quiz Activity", @@ -498,6 +536,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Assessment Plan", @@ -507,6 +546,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Assessment Group", @@ -516,6 +556,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Assessment Result", @@ -525,6 +566,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Assessment Criteria", @@ -541,6 +583,7 @@ "type": "Card Break" }, { + "dependencies": "Assessment Result", "hidden": 0, "is_query_report": 1, "label": "Course wise Assessment Report", @@ -550,6 +593,7 @@ "type": "Link" }, { + "dependencies": "Assessment Result", "hidden": 0, "is_query_report": 1, "label": "Final Assessment Grades", @@ -559,6 +603,7 @@ "type": "Link" }, { + "dependencies": "Assessment Plan", "hidden": 0, "is_query_report": 1, "label": "Assessment Plan Status", @@ -568,6 +613,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Report Generation Tool", @@ -584,6 +630,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Attendance Tool", @@ -593,6 +640,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Assessment Result Tool", @@ -602,6 +650,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student Group Creation Tool", @@ -611,6 +660,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Program Enrollment Tool", @@ -620,6 +670,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course Scheduling Tool", @@ -636,6 +687,7 @@ "type": "Card Break" }, { + "dependencies": "Program Enrollment", "hidden": 0, "is_query_report": 1, "label": "Student and Guardian Contact Details", @@ -645,7 +697,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:40.981880", + "modified": "2020-12-01 12:34:01.016251", "modified_by": "Administrator", "module": "Education", "name": "Education", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json index df4ed76d47..06ae020861 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json @@ -38,6 +38,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Woocommerce Settings", @@ -47,6 +48,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Amazon MWS Settings", @@ -56,6 +58,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shopify Settings", @@ -72,6 +75,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "GoCardless Settings", @@ -81,6 +85,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "M-Pesa Settings", @@ -97,6 +102,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Plaid Settings", @@ -106,6 +112,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Exotel Settings", @@ -115,7 +122,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:38.150378", + "modified": "2020-12-01 12:34:03.534985", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json index 0150a0d530..11a8fd4145 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -28,6 +28,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Woocommerce Settings", @@ -37,6 +38,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shopify Settings", @@ -46,6 +48,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Amazon MWS Settings", @@ -55,6 +58,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Plaid Settings", @@ -64,6 +68,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Exotel Settings", @@ -73,7 +78,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:36.389137", + "modified": "2020-12-01 12:34:05.074849", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations Settings", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index baf7ad86e6..7eec79fc51 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -74,6 +74,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient", @@ -83,6 +84,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Healthcare Practitioner", @@ -92,6 +94,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Practitioner Schedule", @@ -101,6 +104,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Medical Department", @@ -110,6 +114,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Healthcare Service Unit Type", @@ -119,6 +124,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Healthcare Service Unit", @@ -128,6 +134,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Medical Code Standard", @@ -137,6 +144,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Medical Code", @@ -153,6 +161,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Appointment Type", @@ -162,6 +171,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Clinical Procedure Template", @@ -171,6 +181,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Prescription Dosage", @@ -180,6 +191,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Prescription Duration", @@ -189,6 +201,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Antibiotic", @@ -205,6 +218,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Appointment", @@ -214,6 +228,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Clinical Procedure", @@ -223,6 +238,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Encounter", @@ -232,6 +248,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Vital Signs", @@ -241,6 +258,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Complaint", @@ -250,6 +268,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Diagnosis", @@ -259,6 +278,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fee Validity", @@ -275,6 +295,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Healthcare Settings", @@ -291,6 +312,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lab Test Template", @@ -300,6 +322,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lab Test Sample", @@ -309,6 +332,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lab Test UOM", @@ -318,6 +342,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sensitivity", @@ -334,6 +359,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lab Test", @@ -343,6 +369,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sample Collection", @@ -352,6 +379,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Dosage Form", @@ -368,6 +396,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Exercise Type", @@ -377,6 +406,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Therapy Type", @@ -386,6 +416,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Therapy Plan", @@ -395,6 +426,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Therapy Session", @@ -404,6 +436,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Assessment Template", @@ -413,6 +446,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Assessment", @@ -429,6 +463,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient History", @@ -438,6 +473,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Progress", @@ -447,6 +483,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient Medical Record", @@ -456,6 +493,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Inpatient Record", @@ -472,6 +510,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Patient Appointment Analytics", @@ -481,6 +520,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Lab Test Report", @@ -490,7 +530,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:36.771243", + "modified": "2020-12-01 12:34:04.799604", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 787dbd35dd..eecdd98585 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -103,6 +103,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee", @@ -112,6 +113,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employment Type", @@ -121,6 +123,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Branch", @@ -130,6 +133,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Department", @@ -139,6 +143,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Designation", @@ -148,6 +153,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Grade", @@ -157,6 +163,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Group", @@ -166,6 +173,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Health Insurance", @@ -182,6 +190,7 @@ "type": "Card Break" }, { + "dependencies": "Job Applicant", "hidden": 0, "is_query_report": 0, "label": "Employee Onboarding", @@ -191,6 +200,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Skill Map", @@ -200,6 +210,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Promotion", @@ -209,6 +220,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Transfer", @@ -218,6 +230,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Separation", @@ -227,6 +240,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Onboarding Template", @@ -236,6 +250,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Separation Template", @@ -252,6 +267,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shift Type", @@ -261,6 +277,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shift Request", @@ -270,6 +287,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shift Assignment", @@ -286,6 +304,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Leave Application", @@ -295,6 +314,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Leave Allocation", @@ -304,6 +324,7 @@ "type": "Link" }, { + "dependencies": "Leave Type", "hidden": 0, "is_query_report": 0, "label": "Leave Policy", @@ -313,6 +334,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Leave Period", @@ -322,6 +344,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Leave Type", @@ -331,6 +354,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Holiday List", @@ -340,6 +364,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Compensatory Leave Request", @@ -349,6 +374,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Leave Encashment", @@ -358,6 +384,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Leave Block List", @@ -367,6 +394,7 @@ "type": "Link" }, { + "dependencies": "Leave Application", "hidden": 0, "is_query_report": 1, "label": "Employee Leave Balance", @@ -383,6 +411,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Structure", @@ -392,6 +421,7 @@ "type": "Link" }, { + "dependencies": "Salary Structure, Employee", "hidden": 0, "is_query_report": 0, "label": "Salary Structure Assignment", @@ -401,6 +431,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payroll Entry", @@ -410,6 +441,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Slip", @@ -419,6 +451,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payroll Period", @@ -428,6 +461,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Income Tax Slab", @@ -437,6 +471,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Component", @@ -446,6 +481,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Additional Salary", @@ -455,6 +491,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Retention Bonus", @@ -464,6 +501,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Incentive", @@ -473,6 +511,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Salary Register", @@ -489,6 +528,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Attendance Tool", @@ -498,6 +538,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Attendance", @@ -507,6 +548,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Attendance Request", @@ -516,6 +558,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Upload Attendance", @@ -525,6 +568,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Checkin", @@ -534,6 +578,7 @@ "type": "Link" }, { + "dependencies": "Attendance", "hidden": 0, "is_query_report": 1, "label": "Monthly Attendance Sheet", @@ -550,6 +595,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Expense Claim", @@ -559,6 +605,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Advance", @@ -575,6 +622,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "HR Settings", @@ -584,6 +632,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Daily Work Summary Group", @@ -593,6 +642,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Team Updates", @@ -609,6 +659,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Vehicle", @@ -618,6 +669,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Vehicle Log", @@ -627,6 +679,7 @@ "type": "Link" }, { + "dependencies": "Vehicle", "hidden": 0, "is_query_report": 1, "label": "Vehicle Expenses", @@ -643,6 +696,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Job Opening", @@ -652,6 +706,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Job Applicant", @@ -661,6 +716,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Job Offer", @@ -670,6 +726,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Staffing Plan", @@ -686,6 +743,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Loan Application", @@ -695,6 +753,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan", @@ -704,6 +763,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Type", @@ -720,6 +780,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Training Program", @@ -729,6 +790,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Training Event", @@ -738,6 +800,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Training Result", @@ -747,6 +810,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Training Feedback", @@ -763,6 +827,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 1, "label": "Employee Birthday", @@ -772,6 +837,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 1, "label": "Employees working on a holiday", @@ -788,6 +854,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Appraisal", @@ -797,6 +864,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Appraisal Template", @@ -806,6 +874,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Energy Point Rule", @@ -815,6 +884,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Energy Point Log", @@ -831,6 +901,7 @@ "type": "Card Break" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Declaration", @@ -840,6 +911,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Proof Submission", @@ -849,6 +921,7 @@ "type": "Link" }, { + "dependencies": "Employee, Payroll Period", "hidden": 0, "is_query_report": 0, "label": "Employee Other Income", @@ -858,6 +931,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Application", @@ -867,6 +941,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Claim", @@ -876,6 +951,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Category", @@ -885,6 +961,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Sub Category", @@ -894,7 +971,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:43.807222", + "modified": "2020-12-01 12:33:58.806820", "modified_by": "Administrator", "module": "HR", "name": "HR", diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 0c9bd358e9..57b47288c7 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -48,6 +48,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Type", @@ -57,6 +58,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Application", @@ -66,6 +68,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan", @@ -82,6 +85,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Process Loan Security Shortfall", @@ -91,6 +95,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Process Loan Interest Accrual", @@ -107,6 +112,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Disbursement", @@ -116,6 +122,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Repayment", @@ -125,6 +132,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Write Off", @@ -134,6 +142,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Interest Accrual", @@ -150,6 +159,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security Type", @@ -159,6 +169,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security Price", @@ -168,6 +179,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security", @@ -177,6 +189,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security Pledge", @@ -186,6 +199,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security Unpledge", @@ -195,6 +209,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Security Shortfall", @@ -211,6 +226,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Loan Repayment and Closure", @@ -220,6 +236,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Loan Security Status", @@ -229,7 +246,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:39.173544", + "modified": "2020-12-01 12:34:02.390976", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 4eb6f2809f..323f5bfc3d 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -52,6 +52,7 @@ "type": "Card Break" }, { + "dependencies": "Item, BOM", "hidden": 0, "is_query_report": 0, "label": "Work Order", @@ -61,6 +62,7 @@ "type": "Link" }, { + "dependencies": "Item, BOM", "hidden": 0, "is_query_report": 0, "label": "Production Plan", @@ -70,6 +72,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Stock Entry", @@ -79,6 +82,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Job Card", @@ -88,6 +92,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Downtime Entry", @@ -104,6 +109,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item", @@ -113,6 +119,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Bill of Materials", @@ -122,6 +129,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Workstation", @@ -131,6 +139,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Operation", @@ -140,6 +149,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Routing", @@ -156,6 +166,7 @@ "type": "Card Break" }, { + "dependencies": "Work Order", "hidden": 0, "is_query_report": 1, "label": "Production Planning Report", @@ -165,6 +176,7 @@ "type": "Link" }, { + "dependencies": "Work Order", "hidden": 0, "is_query_report": 1, "label": "Work Order Summary", @@ -174,6 +186,7 @@ "type": "Link" }, { + "dependencies": "Quality Inspection", "hidden": 0, "is_query_report": 1, "label": "Quality Inspection Summary", @@ -183,6 +196,7 @@ "type": "Link" }, { + "dependencies": "Downtime Entry", "hidden": 0, "is_query_report": 1, "label": "Downtime Analysis", @@ -192,6 +206,7 @@ "type": "Link" }, { + "dependencies": "Job Card", "hidden": 0, "is_query_report": 1, "label": "Job Card Summary", @@ -201,6 +216,7 @@ "type": "Link" }, { + "dependencies": "BOM", "hidden": 0, "is_query_report": 1, "label": "BOM Search", @@ -210,6 +226,7 @@ "type": "Link" }, { + "dependencies": "BOM", "hidden": 0, "is_query_report": 1, "label": "BOM Stock Report", @@ -219,6 +236,7 @@ "type": "Link" }, { + "dependencies": "Work Order", "hidden": 0, "is_query_report": 1, "label": "Production Analytics", @@ -228,6 +246,7 @@ "type": "Link" }, { + "dependencies": "BOM", "hidden": 0, "is_query_report": 1, "label": "BOM Operations Time", @@ -244,6 +263,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "BOM Update Tool", @@ -253,6 +273,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "BOM Comparison Tool", @@ -269,6 +290,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Manufacturing Settings", @@ -278,7 +300,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:44.426761", + "modified": "2020-12-01 12:33:58.367406", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index 4326250355..1d5292a9c6 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -53,6 +53,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Type", @@ -62,6 +63,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan Application", @@ -71,6 +73,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loan", @@ -87,6 +90,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Grant Application", @@ -103,6 +107,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Member", @@ -112,6 +117,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Membership", @@ -121,6 +127,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Membership Type", @@ -130,6 +137,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Membership Settings", @@ -146,6 +154,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Volunteer", @@ -155,6 +164,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Volunteer Type", @@ -171,6 +181,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chapter", @@ -187,6 +198,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Donor", @@ -196,6 +208,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Donor Type", @@ -205,7 +218,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:42.271279", + "modified": "2020-12-01 12:33:59.867233", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index 0397055184..3b512893b8 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -48,6 +48,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Component", @@ -57,6 +58,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Structure", @@ -66,6 +68,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Structure Assignment", @@ -75,6 +78,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payroll Entry", @@ -84,6 +88,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Slip", @@ -100,6 +105,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Payroll Period", @@ -109,6 +115,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Income Tax Slab", @@ -118,6 +125,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Other Income", @@ -127,6 +135,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Declaration", @@ -136,6 +145,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Proof Submission", @@ -145,6 +155,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Category", @@ -154,6 +165,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Sub Category", @@ -170,6 +182,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Additional Salary", @@ -179,6 +192,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Retention Bonus", @@ -188,6 +202,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Incentive", @@ -197,6 +212,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Application", @@ -206,6 +222,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Claim", @@ -222,6 +239,7 @@ "type": "Card Break" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Salary Register", @@ -231,6 +249,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Salary Payments Based On Payment Mode", @@ -240,6 +259,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Salary Payments via ECS", @@ -249,6 +269,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Income Tax Deductions", @@ -258,6 +279,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Professional Tax Deductions", @@ -267,6 +289,7 @@ "type": "Link" }, { + "dependencies": "Salary Slip", "hidden": 0, "is_query_report": 1, "label": "Provident Fund Deductions", @@ -276,6 +299,7 @@ "type": "Link" }, { + "dependencies": "Payroll Entry", "hidden": 0, "is_query_report": 1, "label": "Bank Remittance", @@ -285,7 +309,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:40.644642", + "modified": "2020-12-01 12:34:01.478995", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 7ad5398251..be49ca7b9e 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -43,6 +43,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Project", @@ -52,6 +53,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Task", @@ -61,6 +63,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Project Template", @@ -70,6 +73,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Project Type", @@ -79,6 +83,7 @@ "type": "Link" }, { + "dependencies": "Project", "hidden": 0, "is_query_report": 0, "label": "Project Update", @@ -95,6 +100,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Timesheet", @@ -104,6 +110,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Activity Type", @@ -113,6 +120,7 @@ "type": "Link" }, { + "dependencies": "Activity Type", "hidden": 0, "is_query_report": 0, "label": "Activity Cost", @@ -129,6 +137,7 @@ "type": "Card Break" }, { + "dependencies": "Timesheet", "hidden": 0, "is_query_report": 1, "label": "Daily Timesheet Summary", @@ -138,6 +147,7 @@ "type": "Link" }, { + "dependencies": "Project", "hidden": 0, "is_query_report": 1, "label": "Project wise Stock Tracking", @@ -147,6 +157,7 @@ "type": "Link" }, { + "dependencies": "Project", "hidden": 0, "is_query_report": 1, "label": "Project Billing Summary", @@ -156,7 +167,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:41.526145", + "modified": "2020-12-01 12:34:00.578885", "modified_by": "Administrator", "module": "Projects", "name": "Projects", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 59a112ec77..1f2c99855b 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -43,6 +43,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Goal", @@ -52,6 +53,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Procedure", @@ -61,6 +63,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Tree of Procedures", @@ -77,6 +80,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Feedback", @@ -86,6 +90,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Feedback Template", @@ -102,6 +107,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Meeting", @@ -118,6 +124,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Non Conformance", @@ -127,6 +134,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Review", @@ -136,6 +144,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Action", @@ -145,7 +154,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:37.174448", + "modified": "2020-12-01 12:34:04.570323", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index 5cb54d315f..1d220bd3ab 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -38,6 +38,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Point-of-Sale Profile", @@ -47,6 +48,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "POS Settings", @@ -63,6 +65,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loyalty Program", @@ -72,6 +75,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Loyalty Point Entry", @@ -88,6 +92,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "POS Opening Entry", @@ -97,6 +102,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "POS Closing Entry", @@ -106,7 +112,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:39.383674", + "modified": "2020-12-01 12:34:02.171186", "modified_by": "Administrator", "module": "Selling", "name": "Retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 29bc549cca..776cb3b62a 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -54,6 +54,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer", @@ -63,6 +64,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Quotation", @@ -72,6 +74,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Sales Order", @@ -81,6 +84,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Sales Invoice", @@ -90,6 +94,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Blanket Order", @@ -99,6 +104,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Sales Partner", @@ -108,6 +114,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Sales Person", @@ -124,6 +131,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item", @@ -133,6 +141,7 @@ "type": "Link" }, { + "dependencies": "Item, Price List", "hidden": 0, "is_query_report": 0, "label": "Item Price", @@ -142,6 +151,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Price List", @@ -151,6 +161,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Group", @@ -160,6 +171,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Product Bundle", @@ -169,6 +181,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Promotional Scheme", @@ -178,6 +191,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", @@ -187,6 +201,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shipping Rule", @@ -196,6 +211,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Coupon Code", @@ -212,6 +228,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Selling Settings", @@ -221,6 +238,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", @@ -230,6 +248,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Taxes and Charges Template", @@ -239,6 +258,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lead Source", @@ -248,6 +268,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer Group", @@ -257,6 +278,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Contact", @@ -266,6 +288,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Address", @@ -275,6 +298,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Territory", @@ -284,6 +308,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Campaign", @@ -300,6 +325,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 1, "label": "Sales Analytics", @@ -309,6 +335,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", @@ -318,6 +345,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Sales Funnel", @@ -327,6 +355,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Order Trends", @@ -336,6 +365,7 @@ "type": "Link" }, { + "dependencies": "Quotation", "hidden": 0, "is_query_report": 1, "label": "Quotation Trends", @@ -345,6 +375,7 @@ "type": "Link" }, { + "dependencies": "Customer", "hidden": 0, "is_query_report": 1, "label": "Customer Acquisition and Loyalty", @@ -354,6 +385,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Inactive Customers", @@ -363,6 +395,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Person-wise Transaction Summary", @@ -372,6 +405,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Item-wise Sales History", @@ -388,6 +422,7 @@ "type": "Card Break" }, { + "dependencies": "Lead", "hidden": 0, "is_query_report": 1, "label": "Lead Details", @@ -397,6 +432,7 @@ "type": "Link" }, { + "dependencies": "Address", "hidden": 0, "is_query_report": 1, "label": "Customer Addresses And Contacts", @@ -406,6 +442,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Available Stock for Packing Items", @@ -415,6 +452,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Pending SO Items For Purchase Request", @@ -424,6 +462,7 @@ "type": "Link" }, { + "dependencies": "Delivery Note", "hidden": 0, "is_query_report": 1, "label": "Delivery Note Trends", @@ -433,6 +472,7 @@ "type": "Link" }, { + "dependencies": "Sales Invoice", "hidden": 0, "is_query_report": 1, "label": "Sales Invoice Trends", @@ -442,6 +482,7 @@ "type": "Link" }, { + "dependencies": "Customer", "hidden": 0, "is_query_report": 1, "label": "Customer Credit Balance", @@ -451,6 +492,7 @@ "type": "Link" }, { + "dependencies": "Customer", "hidden": 0, "is_query_report": 1, "label": "Customers Without Any Sales Transactions", @@ -460,6 +502,7 @@ "type": "Link" }, { + "dependencies": "Customer", "hidden": 0, "is_query_report": 1, "label": "Sales Partners Commission", @@ -469,6 +512,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Territory Target Variance Based On Item Group", @@ -478,6 +522,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Person Target Variance Based On Item Group", @@ -487,6 +532,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Partner Target Variance Based On Item Group", @@ -496,7 +542,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:38.357500", + "modified": "2020-12-01 12:34:03.259945", "modified_by": "Administrator", "module": "Selling", "name": "Selling", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index 03a9fe4b65..0234a3bfb1 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -15,7 +15,7 @@ "is_standard": 1, "label": "ERPNext Settings", "links": [], - "modified": "2020-11-17 13:18:41.371988", + "modified": "2020-12-01 12:34:00.741011", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/desk_page/home/home.json index 6149226f01..702a78c087 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -68,6 +68,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Patient", @@ -77,6 +78,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Diagnosis", @@ -93,6 +95,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Crop", @@ -102,6 +105,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Crop Cycle", @@ -111,6 +115,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Location", @@ -120,6 +125,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Fertilizer", @@ -136,6 +142,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Student", @@ -145,6 +152,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Course", @@ -154,6 +162,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Instructor", @@ -163,6 +172,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Room", @@ -179,6 +189,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Member", @@ -188,6 +199,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Volunteer", @@ -197,6 +209,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chapter", @@ -206,6 +219,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Donor", @@ -222,6 +236,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Warehouse", @@ -231,6 +246,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Brand", @@ -240,6 +256,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Unit of Measure (UOM)", @@ -249,6 +266,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Stock Reconciliation", @@ -265,6 +283,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Employee", @@ -274,6 +293,7 @@ "type": "Link" }, { + "dependencies": "Employee", "hidden": 0, "is_query_report": 0, "label": "Employee Attendance Tool", @@ -283,6 +303,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Salary Structure", @@ -299,6 +320,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Lead", @@ -308,6 +330,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer Group", @@ -317,6 +340,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Territory", @@ -333,6 +357,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item", @@ -342,6 +367,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customer", @@ -351,6 +377,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Supplier", @@ -360,6 +387,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Company", @@ -369,6 +397,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts", @@ -378,6 +407,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Opening Invoice Creation Tool", @@ -394,6 +424,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Import Data", @@ -403,6 +434,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts Importer", @@ -412,6 +444,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Letter Head", @@ -421,6 +454,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Email Account", @@ -430,7 +464,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:41.967994", + "modified": "2020-12-01 12:34:00.132264", "modified_by": "Administrator", "module": "Setup", "name": "Home", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index b545a42254..2713569f4d 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -68,6 +68,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item", @@ -77,6 +78,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Group", @@ -86,6 +88,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Product Bundle", @@ -95,6 +98,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Price List", @@ -104,6 +108,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Price", @@ -113,6 +118,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Shipping Rule", @@ -122,6 +128,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", @@ -131,6 +138,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Alternative", @@ -140,6 +148,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Manufacturer", @@ -149,6 +158,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Customs Tariff Number", @@ -165,6 +175,7 @@ "type": "Card Break" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Material Request", @@ -174,6 +185,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Stock Entry", @@ -183,6 +195,7 @@ "type": "Link" }, { + "dependencies": "Item, Customer", "hidden": 0, "is_query_report": 0, "label": "Delivery Note", @@ -192,6 +205,7 @@ "type": "Link" }, { + "dependencies": "Item, Supplier", "hidden": 0, "is_query_report": 0, "label": "Purchase Receipt", @@ -201,6 +215,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Pick List", @@ -210,6 +225,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Delivery Trip", @@ -226,6 +242,7 @@ "type": "Card Break" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Stock Ledger", @@ -235,6 +252,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Stock Balance", @@ -244,6 +262,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Stock Projected Qty", @@ -253,6 +272,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Stock Summary", @@ -262,6 +282,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Stock Ageing", @@ -271,6 +292,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Item Price Stock", @@ -287,6 +309,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Stock Settings", @@ -296,6 +319,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Warehouse", @@ -305,6 +329,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Unit of Measure (UOM)", @@ -314,6 +339,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Variant Settings", @@ -323,6 +349,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Brand", @@ -332,6 +359,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Item Attribute", @@ -341,6 +369,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "UOM Conversion Factor", @@ -357,6 +386,7 @@ "type": "Card Break" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Serial No", @@ -366,6 +396,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Batch", @@ -375,6 +406,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 0, "label": "Installation Note", @@ -384,6 +416,7 @@ "type": "Link" }, { + "dependencies": "Serial No", "hidden": 0, "is_query_report": 0, "label": "Serial No Service Contract Expiry", @@ -393,6 +426,7 @@ "type": "Link" }, { + "dependencies": "Serial No", "hidden": 0, "is_query_report": 0, "label": "Serial No Status", @@ -402,6 +436,7 @@ "type": "Link" }, { + "dependencies": "Serial No", "hidden": 0, "is_query_report": 0, "label": "Serial No Warranty Expiry", @@ -418,6 +453,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Stock Reconciliation", @@ -427,6 +463,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Landed Cost Voucher", @@ -436,6 +473,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Packing Slip", @@ -445,6 +483,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Inspection", @@ -454,6 +493,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quality Inspection Template", @@ -463,6 +503,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Quick Stock Balance", @@ -479,6 +520,7 @@ "type": "Card Break" }, { + "dependencies": "Item Price", "hidden": 0, "is_query_report": 0, "label": "Item-wise Price List Rate", @@ -488,6 +530,7 @@ "type": "Link" }, { + "dependencies": "Stock Entry", "hidden": 0, "is_query_report": 1, "label": "Stock Analytics", @@ -497,6 +540,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Stock Qty vs Serial No Count", @@ -506,6 +550,7 @@ "type": "Link" }, { + "dependencies": "Delivery Note", "hidden": 0, "is_query_report": 1, "label": "Delivery Note Trends", @@ -515,6 +560,7 @@ "type": "Link" }, { + "dependencies": "Purchase Receipt", "hidden": 0, "is_query_report": 1, "label": "Purchase Receipt Trends", @@ -524,6 +570,7 @@ "type": "Link" }, { + "dependencies": "Sales Order", "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", @@ -533,6 +580,7 @@ "type": "Link" }, { + "dependencies": "Purchase Order", "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", @@ -542,6 +590,7 @@ "type": "Link" }, { + "dependencies": "Bin", "hidden": 0, "is_query_report": 1, "label": "Item Shortage Report", @@ -551,6 +600,7 @@ "type": "Link" }, { + "dependencies": "Batch", "hidden": 0, "is_query_report": 1, "label": "Batch-Wise Balance History", @@ -567,6 +617,7 @@ "type": "Card Break" }, { + "dependencies": "Material Request", "hidden": 0, "is_query_report": 1, "label": "Requested Items To Be Transferred", @@ -576,6 +627,7 @@ "type": "Link" }, { + "dependencies": "Stock Ledger Entry", "hidden": 0, "is_query_report": 1, "label": "Batch Item Expiry Status", @@ -585,6 +637,7 @@ "type": "Link" }, { + "dependencies": "Price List", "hidden": 0, "is_query_report": 1, "label": "Item Prices", @@ -594,6 +647,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Itemwise Recommended Reorder Level", @@ -603,6 +657,7 @@ "type": "Link" }, { + "dependencies": "Item", "hidden": 0, "is_query_report": 1, "label": "Item Variant Details", @@ -612,6 +667,7 @@ "type": "Link" }, { + "dependencies": "Purchase Order", "hidden": 0, "is_query_report": 1, "label": "Subcontracted Raw Materials To Be Transferred", @@ -621,6 +677,7 @@ "type": "Link" }, { + "dependencies": "Purchase Order", "hidden": 0, "is_query_report": 1, "label": "Subcontracted Item To Be Received", @@ -630,6 +687,7 @@ "type": "Link" }, { + "dependencies": "Stock Ledger Entry", "hidden": 0, "is_query_report": 1, "label": "Stock and Account Value Comparison", @@ -639,7 +697,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:38.783880", + "modified": "2020-12-01 12:34:02.711315", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 5a88307ef0..e760ad6bf3 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -53,6 +53,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Issue", @@ -62,6 +63,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Issue Type", @@ -71,6 +73,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Issue Priority", @@ -87,6 +90,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Maintenance Schedule", @@ -96,6 +100,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Maintenance Visit", @@ -112,6 +117,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Service Level Agreement", @@ -128,6 +134,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Warranty Claim", @@ -137,6 +144,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Serial No", @@ -153,6 +161,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Support Settings", @@ -169,6 +178,7 @@ "type": "Card Break" }, { + "dependencies": "Issue", "hidden": 0, "is_query_report": 1, "label": "First Response Time for Issues", @@ -178,7 +188,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:40.457842", + "modified": "2020-12-01 12:34:01.698260", "modified_by": "Administrator", "module": "Support", "name": "Support", diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json index 3f654bea64..2a86dfa158 100644 --- a/erpnext/utilities/desk_page/utilities/utilities.json +++ b/erpnext/utilities/desk_page/utilities/utilities.json @@ -27,6 +27,7 @@ "type": "Card Break" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Video", @@ -36,6 +37,7 @@ "type": "Link" }, { + "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Video Settings", @@ -45,7 +47,7 @@ "type": "Link" } ], - "modified": "2020-11-17 13:18:39.317897", + "modified": "2020-12-01 12:34:02.280074", "modified_by": "Administrator", "module": "Utilities", "name": "Utilities", From a3845a95ed8c9bf7fe3d0ac977324a01f2d92dc2 Mon Sep 17 00:00:00 2001 From: Leela vadlamudi Date: Tue, 1 Dec 2020 13:04:53 +0530 Subject: [PATCH 198/283] feat: Introducing telephony module (#24032) --- .editorconfig | 14 +++ erpnext/hooks.py | 4 +- erpnext/modules.txt | 3 +- erpnext/public/build.json | 3 +- erpnext/public/js/call_popup/call_popup.js | 2 +- erpnext/public/js/telephony.js | 23 ++++ .../call_log => telephony}/__init__.py | 0 erpnext/telephony/doctype/__init__.py | 0 .../telephony/doctype/call_log/__init__.py | 0 .../telephony/doctype/call_log/call_log.js | 8 ++ .../doctype/call_log/call_log.json | 5 +- .../doctype/call_log/call_log.py | 0 .../doctype/call_log/test_call_log.py | 10 ++ .../__init__.py | 0 .../incoming_call_handling_schedule.json | 60 +++++++++++ .../incoming_call_handling_schedule.py | 10 ++ .../incoming_call_settings/__init__.py | 0 .../incoming_call_settings.js | 102 ++++++++++++++++++ .../incoming_call_settings.json | 82 ++++++++++++++ .../incoming_call_settings.py | 63 +++++++++++ .../test_incoming_call_settings.py | 10 ++ 21 files changed, 391 insertions(+), 8 deletions(-) create mode 100644 .editorconfig create mode 100644 erpnext/public/js/telephony.js rename erpnext/{communication/doctype/call_log => telephony}/__init__.py (100%) create mode 100644 erpnext/telephony/doctype/__init__.py create mode 100644 erpnext/telephony/doctype/call_log/__init__.py create mode 100644 erpnext/telephony/doctype/call_log/call_log.js rename erpnext/{communication => telephony}/doctype/call_log/call_log.json (97%) rename erpnext/{communication => telephony}/doctype/call_log/call_log.py (100%) create mode 100644 erpnext/telephony/doctype/call_log/test_call_log.py create mode 100644 erpnext/telephony/doctype/incoming_call_handling_schedule/__init__.py create mode 100644 erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json create mode 100644 erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py create mode 100644 erpnext/telephony/doctype/incoming_call_settings/__init__.py create mode 100644 erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js create mode 100644 erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.json create mode 100644 erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py create mode 100644 erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..24f122a8d4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Root editor config file +root = true + +# Common settings +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# python, js indentation settings +[{*.py,*.js}] +indent_style = tab +indent_size = 4 diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 726ab6e22a..987345697a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -271,11 +271,11 @@ doc_events = { }, "Contact": { "on_trash": "erpnext.support.doctype.issue.issue.update_issue", - "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information", + "after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information", "validate": "erpnext.crm.utils.update_lead_phone_numbers" }, "Lead": { - "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information" + "after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information" }, "Email Unsubscribe": { "after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient" diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 1e2aeea36a..62f5dce846 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -25,4 +25,5 @@ Hub Node Quality Management Communication Loan Management -Payroll \ No newline at end of file +Payroll +Telephony \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 2695502269..2f15cbcef1 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -49,7 +49,8 @@ "public/js/education/assessment_result_tool.html", "public/js/hub/hub_factory.js", "public/js/call_popup/call_popup.js", - "public/js/utils/dimension_tree_filter.js" + "public/js/utils/dimension_tree_filter.js", + "public/js/telephony.js" ], "js/item-dashboard.min.js": [ "stock/dashboard/item_dashboard.html", diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 5e4d4a585f..aeb3b387f2 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -74,7 +74,7 @@ class CallPopup { 'click': () => { const call_summary = this.dialog.get_value('call_summary'); if (!call_summary) return; - frappe.xcall('erpnext.communication.doctype.call_log.call_log.add_call_summary', { + frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', { 'call_log': this.call_log.name, 'summary': call_summary, }).then(() => { diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js new file mode 100644 index 0000000000..bd7f890306 --- /dev/null +++ b/erpnext/public/js/telephony.js @@ -0,0 +1,23 @@ +frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( { + make_input() { + this._super(); + if (this.df.options == 'Phone') { + this.setup_phone(); + } + }, + setup_phone() { + if (frappe.phone_call.handler) { + this.$wrapper.find('.control-input') + .append(` + + + + + `) + .find('.phone-btn') + .click(() => { + frappe.phone_call.handler(this.get_value(), this.frm); + }); + } + } +}); \ No newline at end of file diff --git a/erpnext/communication/doctype/call_log/__init__.py b/erpnext/telephony/__init__.py similarity index 100% rename from erpnext/communication/doctype/call_log/__init__.py rename to erpnext/telephony/__init__.py diff --git a/erpnext/telephony/doctype/__init__.py b/erpnext/telephony/doctype/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/call_log/__init__.py b/erpnext/telephony/doctype/call_log/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/call_log/call_log.js b/erpnext/telephony/doctype/call_log/call_log.js new file mode 100644 index 0000000000..977f86da0d --- /dev/null +++ b/erpnext/telephony/doctype/call_log/call_log.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('Call Log', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/communication/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json similarity index 97% rename from erpnext/communication/doctype/call_log/call_log.json rename to erpnext/telephony/doctype/call_log/call_log.json index 31e79f17cd..55ad2baefd 100644 --- a/erpnext/communication/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -137,12 +137,11 @@ "read_only": 1 } ], - "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-25 17:08:34.085731", + "modified": "2020-11-25 14:32:44.407815", "modified_by": "Administrator", - "module": "Communication", + "module": "Telephony", "name": "Call Log", "owner": "Administrator", "permissions": [ diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py similarity index 100% rename from erpnext/communication/doctype/call_log/call_log.py rename to erpnext/telephony/doctype/call_log/call_log.py diff --git a/erpnext/telephony/doctype/call_log/test_call_log.py b/erpnext/telephony/doctype/call_log/test_call_log.py new file mode 100644 index 0000000000..faa63041ba --- /dev/null +++ b/erpnext/telephony/doctype/call_log/test_call_log.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 TestCallLog(unittest.TestCase): + pass diff --git a/erpnext/telephony/doctype/incoming_call_handling_schedule/__init__.py b/erpnext/telephony/doctype/incoming_call_handling_schedule/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json new file mode 100644 index 0000000000..6d46b4e2cd --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "creation": "2020-11-19 11:15:54.967710", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day_of_week", + "from_time", + "to_time", + "agent_group" + ], + "fields": [ + { + "fieldname": "day_of_week", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day Of Week", + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday", + "reqd": 1 + }, + { + "default": "9:00:00", + "fieldname": "from_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "From Time", + "reqd": 1 + }, + { + "default": "17:00:00", + "fieldname": "to_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "To Time", + "reqd": 1 + }, + { + "fieldname": "agent_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Agent Group", + "options": "Employee Group", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-19 11:15:54.967710", + "modified_by": "Administrator", + "module": "Telephony", + "name": "Incoming Call Handling Schedule", + "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/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.py new file mode 100644 index 0000000000..fcf29745e2 --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_handling_schedule/incoming_call_handling_schedule.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 IncomingCallHandlingSchedule(Document): + pass diff --git a/erpnext/telephony/doctype/incoming_call_settings/__init__.py b/erpnext/telephony/doctype/incoming_call_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js new file mode 100644 index 0000000000..1bcc846132 --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js @@ -0,0 +1,102 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +function time_to_seconds(time_str) { + // Convert time string of format HH:MM:SS into seconds. + let seq = time_str.split(':'); + seq = seq.map((n) => parseInt(n)); + return (seq[0]*60*60) + (seq[1]*60) + seq[2]; +} + +function number_sort(array, ascending=true) { + let array_copy = [...array]; + if (ascending) { + array_copy.sort((a, b) => a-b); // ascending order + } else { + array_copy.sort((a, b) => b-a); // descending order + } + return array_copy; +} + +function groupby(items, key) { + // Group the list of items using the given key. + const obj = {}; + items.forEach((item) => { + if (item[key] in obj) { + obj[item[key]].push(item); + } else { + obj[item[key]] = [item]; + } + }); + return obj; +} + +function check_timeslot_overlap(ts1, ts2) { + /// Timeslot is a an array of length 2 ex: [from_time, to_time] + /// time in timeslot is an integer represents number of seconds. + if ((ts1[0] < ts2[0] && ts1[1] <= ts2[0]) || (ts1[0] >= ts2[1] && ts1[1] > ts2[1])) { + return false; + } + return true; +} + +function validate_call_schedule(schedule) { + validate_call_schedule_timeslot(schedule); + validate_call_schedule_overlaps(schedule); +} + +function validate_call_schedule_timeslot(schedule) { + // Make sure that to time slot is ahead of from time slot. + let errors = []; + + for (let row in schedule) { + let record = schedule[row]; + let from_time_in_secs = time_to_seconds(record.from_time); + let to_time_in_secs = time_to_seconds(record.to_time); + if (from_time_in_secs >= to_time_in_secs) { + errors.push(__('Call Schedule Row {0}: To time slot should always be ahead of From time slot.', [row])); + } + } + + if (errors.length > 0) { + frappe.throw(errors.join("
")); + } +} + +function is_call_schedule_overlapped(day_schedule) { + // Check if any time slots are overlapped in a day schedule. + let timeslots = []; + day_schedule.forEach((record)=> { + timeslots.push([time_to_seconds(record.from_time), time_to_seconds(record.to_time)]); + }); + + if (timeslots.length < 2) { + return false; + } + + timeslots = number_sort(timeslots); + + // Sorted timeslots will be in ascending order if not overlapped. + for (let i=1; i < timeslots.length; i++) { + if (check_timeslot_overlap(timeslots[i-1], timeslots[i])) { + return true; + } + } + return false; +} + +function validate_call_schedule_overlaps(schedule) { + let group_by_day = groupby(schedule, 'day_of_week'); + for (const [day, day_schedule] of Object.entries(group_by_day)) { + if (is_call_schedule_overlapped(day_schedule)) { + frappe.throw(__('Please fix overlapping time slots for {0}', [day])); + } + } +} + +frappe.ui.form.on('Incoming Call Settings', { + validate(frm) { + validate_call_schedule(frm.doc.call_handling_schedule); + } +}); + diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.json b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.json new file mode 100644 index 0000000000..3ffb3e49db --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.json @@ -0,0 +1,82 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-11-19 10:37:20.734245", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "call_routing", + "column_break_2", + "greeting_message", + "agent_busy_message", + "agent_unavailable_message", + "section_break_6", + "call_handling_schedule" + ], + "fields": [ + { + "default": "Sequential", + "fieldname": "call_routing", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Call Routing", + "options": "Sequential\nSimultaneous" + }, + { + "fieldname": "greeting_message", + "fieldtype": "Data", + "label": "Greeting Message" + }, + { + "fieldname": "agent_busy_message", + "fieldtype": "Data", + "label": "Agent Busy Message" + }, + { + "fieldname": "agent_unavailable_message", + "fieldtype": "Data", + "label": "Agent Unavailable Message" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "call_handling_schedule", + "fieldtype": "Table", + "label": "Call Handling Schedule", + "options": "Incoming Call Handling Schedule", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-11-19 11:17:14.527862", + "modified_by": "Administrator", + "module": "Telephony", + "name": "Incoming Call Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py new file mode 100644 index 0000000000..2b2008a8ab --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.py @@ -0,0 +1,63 @@ +# -*- 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 +from datetime import datetime +from typing import Tuple +from frappe import _ + +class IncomingCallSettings(Document): + def validate(self): + """List of validations + * Make sure that to time slot is ahead of from time slot in call schedule + * Make sure that no overlapping timeslots for a given day + """ + self.validate_call_schedule_timeslot(self.call_handling_schedule) + self.validate_call_schedule_overlaps(self.call_handling_schedule) + + def validate_call_schedule_timeslot(self, schedule: list): + """ Make sure that to time slot is ahead of from time slot. + """ + errors = [] + for record in schedule: + from_time = self.time_to_seconds(record.from_time) + to_time = self.time_to_seconds(record.to_time) + if from_time >= to_time: + errors.append( + _('Call Schedule Row {0}: To time slot should always be ahead of From time slot.').format(record.idx) + ) + + if errors: + frappe.throw('
'.join(errors)) + + def validate_call_schedule_overlaps(self, schedule: list): + """Check if any time slots are overlapped in a day schedule. + """ + week_days = set([each.day_of_week for each in schedule]) + + for day in week_days: + timeslots = [(record.from_time, record.to_time) for record in schedule if record.day_of_week==day] + + # convert time in timeslot into an integer represents number of seconds + timeslots = sorted(map(lambda seq: tuple(map(self.time_to_seconds, seq)), timeslots)) + if len(timeslots) < 2: continue + + for i in range(1, len(timeslots)): + if self.check_timeslots_overlap(timeslots[i-1], timeslots[i]): + frappe.throw(_('Please fix overlapping time slots for {0}.').format(day)) + + @staticmethod + def check_timeslots_overlap(ts1: Tuple[int, int], ts2: Tuple[int, int]) -> bool: + if (ts1[0] < ts2[0] and ts1[1] <= ts2[0]) or (ts1[0] >= ts2[1] and ts1[1] > ts2[1]): + return False + return True + + @staticmethod + def time_to_seconds(time: str) -> int: + """Convert time string of format HH:MM:SS into seconds + """ + date_time = datetime.strptime(time, "%H:%M:%S") + return date_time - datetime(1900, 1, 1) diff --git a/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py b/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.py new file mode 100644 index 0000000000..c058c117b3 --- /dev/null +++ b/erpnext/telephony/doctype/incoming_call_settings/test_incoming_call_settings.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 TestIncomingCallSettings(unittest.TestCase): + pass From 9aaca25edb6aa740ecd64fa1caf9162a7342b1ba Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 1 Dec 2020 13:39:54 +0530 Subject: [PATCH 199/283] feat: update desk page json --- .../desk_page/accounting/accounting.json | 84 +------------------ .../desk_page/agriculture/agriculture.json | 19 +---- erpnext/assets/desk_page/assets/assets.json | 19 +---- erpnext/buying/desk_page/buying/buying.json | 44 +--------- erpnext/crm/desk_page/crm/crm.json | 29 +------ .../desk_page/education/education.json | 69 +-------------- .../erpnext_integrations.json | 19 +---- .../erpnext_integrations_settings.json | 9 +- .../desk_page/healthcare/healthcare.json | 49 +---------- erpnext/hr/desk_page/hr/hr.json | 79 +---------------- .../loan_management/desk_page/loan/loan.json | 29 +------ .../manufacturing/manufacturing.json | 29 +------ .../desk_page/non_profit/non_profit.json | 34 +------- .../payroll/desk_page/payroll/payroll.json | 24 +----- .../projects/desk_page/projects/projects.json | 19 +---- .../desk_page/quality/quality.json | 24 +----- erpnext/selling/desk_page/retail/retail.json | 19 +---- .../selling/desk_page/selling/selling.json | 29 +------ .../erpnext_settings/erpnext_settings.json | 3 +- erpnext/setup/desk_page/home/home.json | 49 +---------- erpnext/stock/desk_page/stock/stock.json | 44 +--------- .../support/desk_page/support/support.json | 34 +------- .../desk_page/utilities/utilities.json | 9 +- 23 files changed, 23 insertions(+), 743 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index ccc51685db..0add27786b 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -1,86 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Accounting Masters", - "links": "[\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounts Settings\",\n \"name\": \"Accounts Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Financial / accounting year.\",\n \"label\": \"Fiscal Year\",\n \"name\": \"Fiscal Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Finance Book\",\n \"name\": \"Finance Book\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Period\",\n \"name\": \"Accounting Period\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Terms based on conditions\",\n \"label\": \"Payment Term\",\n \"name\": \"Payment Term\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "General Ledger", - "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make journal entries from a template.\",\n \"label\": \"Journal Entry Template\",\n \"name\": \"Journal Entry Template\",\n \"type\": \"doctype\"\n },\n \n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Accounts Receivable", - "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\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 \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" - }, - { - "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 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, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\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 \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Financial Statements", - "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance\",\n \"name\": \"Trial Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profit and Loss Statement\",\n \"name\": \"Profit and Loss Statement\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Balance Sheet\",\n \"name\": \"Balance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Cash Flow\",\n \"name\": \"Cash Flow\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Consolidated Financial Statement\",\n \"name\": \"Consolidated Financial Statement\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Multi Currency", - "links": "[\n {\n \"description\": \"Enable / disable currencies.\",\n \"label\": \"Currency\",\n \"name\": \"Currency\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Currency exchange rate master.\",\n \"label\": \"Currency Exchange\",\n \"name\": \"Currency Exchange\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exchange Rate Revaluation master.\",\n \"label\": \"Exchange Rate Revaluation\",\n \"name\": \"Exchange Rate Revaluation\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"description\": \"Setup Gateway accounts.\",\n \"label\": \"Payment Gateway Account\",\n \"name\": \"Payment Gateway Account\",\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 {\n \"description\": \"e.g. Bank, Cash, Credit Card\",\n \"label\": \"Mode of Payment\",\n \"name\": \"Mode of Payment\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "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 Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\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, - "label": "Subscription Management", - "links": "[\n {\n \"label\": \"Subscription Plan\",\n \"name\": \"Subscription Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription\",\n \"name\": \"Subscription\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription Settings\",\n \"name\": \"Subscription Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Goods and Services Tax (GST India)", - "links": "[\n {\n \"label\": \"GST Settings\",\n \"name\": \"GST Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"GST HSN Code\",\n \"name\": \"GST HSN Code\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-1\",\n \"name\": \"GSTR-1\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-2\",\n \"name\": \"GSTR-2\",\n \"type\": \"report\"\n },\n {\n \"label\": \"GSTR 3B Report\",\n \"name\": \"GSTR 3B Report\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Sales Register\",\n \"name\": \"GST Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Purchase Register\",\n \"name\": \"GST Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Sales Register\",\n \"name\": \"GST Itemised Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Purchase Register\",\n \"name\": \"GST Itemised Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"country\": \"India\",\n \"description\": \"C-Form records\",\n \"label\": \"C-Form\",\n \"name\": \"C-Form\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"India\",\n \"label\": \"Lower Deduction Certificate\",\n \"name\": \"Lower Deduction Certificate\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Share Management", - "links": "[\n {\n \"description\": \"List of available Shareholders with folio numbers\",\n \"label\": \"Shareholder\",\n \"name\": \"Shareholder\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of all share transactions\",\n \"label\": \"Share Transfer\",\n \"name\": \"Share Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Ledger\",\n \"name\": \"Share Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Balance\",\n \"name\": \"Share Balance\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Cost Center and Budgeting", - "links": "[\n {\n \"description\": \"Tree of financial Cost Centers.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Cost Centers\",\n \"name\": \"Cost Center\",\n \"route\": \"#Tree/Cost Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define budget for a financial year.\",\n \"label\": \"Budget\",\n \"name\": \"Budget\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Cost Center\"\n ],\n \"doctype\": \"Cost Center\",\n \"is_query_report\": true,\n \"label\": \"Budget Variance Report\",\n \"name\": \"Budget Variance Report\",\n \"type\": \"report\"\n },\n {\n \"description\": \"Seasonality for setting budgets, targets etc.\",\n \"label\": \"Monthly Distribution\",\n \"name\": \"Monthly Distribution\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Opening and Closing", - "links": "[\n {\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Close Balance Sheet and book Profit or Loss.\",\n \"label\": \"Period Closing Voucher\",\n \"name\": \"Period Closing Voucher\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Taxes", - "links": "[\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\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\": \"Tax template for item tax rates.\",\n \"label\": \"Item Tax Template\",\n \"name\": \"Item Tax Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Category for overriding tax rates.\",\n \"label\": \"Tax Category\",\n \"name\": \"Tax Category\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Rule for transactions.\",\n \"label\": \"Tax Rule\",\n \"name\": \"Tax Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Withholding rates to be applied on transactions.\",\n \"label\": \"Tax Withholding Category\",\n \"name\": \"Tax Withholding Category\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Profitability", - "links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -1143,7 +1061,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:03.892648", + "modified": "2020-12-01 13:38:35.349024", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 38da1c43e8..26829cbb1b 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -1,21 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Crops & Lands", - "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Analytics", - "links": "[\n {\n \"label\": \"Plant Analysis\",\n \"name\": \"Plant Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Analysis\",\n \"name\": \"Soil Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Water Analysis\",\n \"name\": \"Water Analysis\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Soil Texture\",\n \"name\": \"Soil Texture\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Weather\",\n \"name\": \"Weather\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Agriculture Analysis Criteria\",\n \"name\": \"Agriculture Analysis Criteria\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Diseases & Fertilizers", - "links": "[\n {\n \"label\": \"Disease\",\n \"name\": \"Disease\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Domains", "charts": [], "creation": "2020-03-02 17:23:34.339274", @@ -162,7 +145,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:33:59.698335", + "modified": "2020-12-01 13:38:38.477493", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 54572156d7..58bdc68ef2 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -1,21 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Assets", - "links": "[\n {\n \"label\": \"Asset\",\n \"name\": \"Asset\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Asset Category\",\n \"name\": \"Asset Category\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Transfer an asset from one warehouse to another\",\n \"label\": \"Asset Movement\",\n \"name\": \"Asset Movement\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Maintenance", - "links": "[\n {\n \"label\": \"Asset Maintenance Team\",\n \"name\": \"Asset Maintenance Team\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance Team\"\n ],\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"label\": \"Asset Maintenance Log\",\n \"name\": \"Asset Maintenance Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Value Adjustment\",\n \"name\": \"Asset Value Adjustment\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"label\": \"Asset Repair\",\n \"name\": \"Asset Repair\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciation Ledger\",\n \"name\": \"Asset Depreciation Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset\"\n ],\n \"doctype\": \"Asset\",\n \"is_query_report\": true,\n \"label\": \"Asset Depreciations and Balances\",\n \"name\": \"Asset Depreciations and Balances\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Asset Maintenance\"\n ],\n \"doctype\": \"Asset Maintenance\",\n \"label\": \"Asset Maintenance\",\n \"name\": \"Asset Maintenance\",\n \"type\": \"report\"\n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -177,7 +160,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:00.417242", + "modified": "2020-12-01 13:38:37.977119", "modified_by": "Administrator", "module": "Assets", "name": "Assets", diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index e25554c796..8e00f7fd9a 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -1,46 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "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": "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", - "links": "[\n {\n \"description\": \"All Supplier scorecards.\",\n \"label\": \"Supplier Scorecard\",\n \"name\": \"Supplier Scorecard\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard variables.\",\n \"label\": \"Supplier Scorecard Variable\",\n \"name\": \"Supplier Scorecard Variable\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier scorecard criteria.\",\n \"label\": \"Supplier Scorecard Criteria\",\n \"name\": \"Supplier Scorecard Criteria\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Templates of supplier standings.\",\n \"label\": \"Supplier Scorecard Standing\",\n \"name\": \"Supplier Scorecard Standing\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "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\": \"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\": \"Items to Order and Receive\",\n \"name\": \"Requested Items to Order and Receive\",\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\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\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\": \"Supplier Quotation Comparison\",\n \"name\": \"Supplier Quotation 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": "", "category": "Modules", "charts": [ @@ -509,7 +467,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:33:59.349265", + "modified": "2020-12-01 13:38:38.615167", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 8679065bca..724ff77b05 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -1,31 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Sales Pipeline", - "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Potential opportunities for selling.\",\n \"label\": \"Opportunity\",\n \"name\": \"Opportunity\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Record of all communications of type email, phone, chat, visit, etc.\",\n \"label\": \"Communication\",\n \"name\": \"Communication\",\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\": \"Helps you keep tracks of Contracts based on Supplier, Customer and Employee\",\n \"label\": \"Contract\",\n \"name\": \"Contract\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Helps you manage appointments with your leads\",\n \"label\": \"Appointment\",\n \"name\": \"Appointment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "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 \"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 \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Prospects Engaged But Not Converted\",\n \"name\": \"Prospects Engaged But Not Converted\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Opportunity\"\n ],\n \"doctype\": \"Opportunity\",\n \"is_query_report\": true,\n \"label\": \"First Response Time for Opportunity\",\n \"name\": \"First Response Time for Opportunity\",\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 \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Campaign Efficiency\",\n \"name\": \"Campaign Efficiency\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Owner Efficiency\",\n \"name\": \"Lead Owner Efficiency\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Maintenance", - "links": "[\n {\n \"description\": \"Plan for maintenance visits.\",\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Visit report for maintenance call.\",\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Campaign", - "links": "[\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sends Mails to lead or contact based on a Campaign schedule\",\n \"label\": \"Email Campaign\",\n \"name\": \"Email Campaign\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create and Schedule social media posts\",\n \"label\": \"Social Media Post\",\n \"name\": \"Social Media Post\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\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 \"onboard\": 1,\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 \"onboard\": 1,\n \"type\": \"doctype\"\n },\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 \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Send mass SMS to your contacts\",\n \"label\": \"SMS Center\",\n \"name\": \"SMS Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Logs for maintaining sms delivery status\",\n \"label\": \"SMS Log\",\n \"name\": \"SMS Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup SMS gateway settings\",\n \"label\": \"SMS Settings\",\n \"name\": \"SMS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Twitter Settings\",\n \"name\": \"Twitter Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"LinkedIn Settings\",\n \"name\": \"LinkedIn Settings\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -390,7 +363,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:01.909417", + "modified": "2020-12-01 13:38:36.871352", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index a4bdb21ae6..04db5a4366 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -1,71 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Student and Instructor", - "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Guardian\",\n \"name\": \"Guardian\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group\",\n \"name\": \"Student Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Log\",\n \"name\": \"Student Log\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Masters", - "links": "[\n {\n \"label\": \"Program\",\n \"name\": \"Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Topic\",\n \"name\": \"Topic\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Content Masters", - "links": "[\n {\n \"label\": \"Article\",\n \"name\": \"Article\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Video\",\n \"name\": \"Video\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz\",\n \"name\": \"Quiz\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"label\": \"Education Settings\",\n \"name\": \"Education Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Category\",\n \"name\": \"Student Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Batch Name\",\n \"name\": \"Student Batch Name\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Grading Scale\",\n \"name\": \"Grading Scale\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Term\",\n \"name\": \"Academic Term\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Academic Year\",\n \"name\": \"Academic Year\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Admission", - "links": "[\n {\n \"label\": \"Student Applicant\",\n \"name\": \"Student Applicant\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Admission\",\n \"name\": \"Student Admission\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment\",\n \"name\": \"Program Enrollment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Enrollment\",\n \"name\": \"Course Enrollment\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Fees", - "links": "[\n {\n \"label\": \"Fee Structure\",\n \"name\": \"Fee Structure\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Category\",\n \"name\": \"Fee Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fee Schedule\",\n \"name\": \"Fee Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fees\",\n \"name\": \"Fees\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Student Fee Collection Report\",\n \"name\": \"Student Fee Collection\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Program wise Fee Collection Report\",\n \"name\": \"Program wise Fee Collection\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Schedule", - "links": "[\n {\n \"label\": \"Course Schedule\",\n \"name\": \"Course Schedule\",\n \"route\": \"#List/Course Schedule/Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Attendance", - "links": "[\n {\n \"label\": \"Student Attendance\",\n \"name\": \"Student Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Leave Application\",\n \"name\": \"Student Leave Application\",\n \"type\": \"doctype\"\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 \"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]" - }, - { - "hidden": 0, - "label": "LMS Activity", - "links": "[\n {\n \"label\": \"Course Enrollment\",\n \"name\": \"Course Enrollment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Activity\",\n \"name\": \"Course Activity\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quiz Activity\",\n \"name\": \"Quiz Activity\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Assessment", - "links": "[\n {\n \"label\": \"Assessment Plan\",\n \"name\": \"Assessment Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Group\",\n \"link\": \"Tree/Assessment Group\",\n \"name\": \"Assessment Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result\",\n \"name\": \"Assessment Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Criteria\",\n \"name\": \"Assessment Criteria\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "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": "Tools", - "links": "[\n {\n \"label\": \"Student Attendance Tool\",\n \"name\": \"Student Attendance Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Assessment Result Tool\",\n \"name\": \"Assessment Result Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Student Group Creation Tool\",\n \"name\": \"Student Group Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Program Enrollment Tool\",\n \"name\": \"Program Enrollment Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course Scheduling Tool\",\n \"name\": \"Course Scheduling Tool\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Other Reports", - "links": "[\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]" - } - ], "category": "Domains", "charts": [ { @@ -697,7 +630,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:01.016251", + "modified": "2020-12-01 13:38:37.448989", "modified_by": "Administrator", "module": "Education", "name": "Education", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json index 06ae020861..a36ca8ddb7 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json @@ -1,21 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Marketplace", - "links": "[\n {\n \"description\": \"Woocommerce marketplace settings\",\n \"label\": \"Woocommerce Settings\",\n \"name\": \"Woocommerce Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Amazon MWS settings\",\n \"label\": \"Amazon MWS Settings\",\n \"name\": \"Amazon MWS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Shopify settings\",\n \"label\": \"Shopify Settings\",\n \"name\": \"Shopify Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Payments", - "links": "[\n {\n \"description\": \"GoCardless payment gateway settings\",\n \"label\": \"GoCardless Settings\",\n \"name\": \"GoCardless Settings\",\n \"type\": \"doctype\"\n }, {\n \"description\": \"M-Pesa payment gateway settings\",\n \"label\": \"M-Pesa Settings\",\n \"name\": \"Mpesa Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"description\": \"Plaid settings\",\n \"label\": \"Plaid Settings\",\n \"name\": \"Plaid Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exotel settings\",\n \"label\": \"Exotel Settings\",\n \"name\": \"Exotel Settings\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-08-20 19:30:48.138801", @@ -122,7 +105,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:03.534985", + "modified": "2020-12-01 13:38:35.846528", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json index 11a8fd4145..d5e6189243 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -1,11 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Integrations Settings", - "links": "[\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Woocommerce Settings\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Shopify Settings\",\n\t\t\"description\": \"Connect Shopify with ERPNext\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Amazon MWS Settings\",\n\t\t\"description\": \"Connect Amazon with ERPNext\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Plaid Settings\",\n\t\t\"description\": \"Connect your bank accounts to ERPNext\"\n\t},\n {\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Exotel Settings\",\n\t\t\"description\": \"Connect your Exotel Account to ERPNext and track call logs\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-07-31 10:38:54.021237", @@ -78,7 +71,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:05.074849", + "modified": "2020-12-01 13:38:34.732552", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations Settings", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 7eec79fc51..cf1f6246cb 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -1,51 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Masters", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient\",\n\t\t\"label\": \"Patient\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Practitioner\",\n\t\t\"label\":\"Healthcare Practitioner\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Practitioner Schedule\",\n\t\t\"label\": \"Practitioner Schedule\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Department\",\n\t\t\"label\": \"Medical Department\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit Type\",\n\t\t\"label\": \"Healthcare Service Unit Type\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Service Unit\",\n\t\t\"label\": \"Healthcare Service Unit\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code Standard\",\n\t\t\"label\": \"Medical Code Standard\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Medical Code\",\n\t\t\"label\": \"Medical Code\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Consultation Setup", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Appointment Type\",\n\t\t\"label\": \"Appointment Type\"\n },\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure Template\",\n\t\t\"label\": \"Clinical Procedure Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Dosage\",\n\t\t\"label\": \"Prescription Dosage\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Prescription Duration\",\n\t\t\"label\": \"Prescription Duration\"\n\t},\n\t{\n\t \"type\": \"doctype\",\n\t\t\"name\": \"Antibiotic\",\n\t\t\"label\": \"Antibiotic\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Consultation", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Appointment\",\n\t\t\"label\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Clinical Procedure\",\n\t\t\"label\": \"Clinical Procedure\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Encounter\",\n\t\t\"label\": \"Patient Encounter\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Vital Signs\",\n\t\t\"label\": \"Vital Signs\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Complaint\",\n\t\t\"label\": \"Complaint\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Diagnosis\",\n\t\t\"label\": \"Diagnosis\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Fee Validity\",\n\t\t\"label\": \"Fee Validity\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Healthcare Settings\",\n\t\t\"label\": \"Healthcare Settings\",\n\t\t\"onboard\": 1\n\t}\n]" - }, - { - "hidden": 0, - "label": "Laboratory Setup", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Template\",\n\t\t\"label\": \"Lab Test Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test Sample\",\n\t\t\"label\": \"Lab Test Sample\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test UOM\",\n\t\t\"label\": \"Lab Test UOM\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sensitivity\",\n\t\t\"label\": \"Sensitivity\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Laboratory", - "links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Rehabilitation and Physiotherapy", - "links": "[\n {\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Exercise Type\",\n\t\t\"label\": \"Exercise Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Type\",\n\t\t\"label\": \"Therapy Type\",\n\t\t\"onboard\": 1\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Plan\",\n\t\t\"label\": \"Therapy Plan\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Therapy Session\",\n\t\t\"label\": \"Therapy Session\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment Template\",\n\t\t\"label\": \"Patient Assessment Template\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Assessment\",\n\t\t\"label\": \"Patient Assessment\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Records and History", - "links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]" - } - ], "category": "Domains", "charts": [ { @@ -530,7 +483,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:04.799604", + "modified": "2020-12-01 13:38:34.841396", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index eecdd98585..218699314c 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -1,81 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Employee", - "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employment Type\",\n \"name\": \"Employment Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Branch\",\n \"name\": \"Branch\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Department\",\n \"name\": \"Department\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Designation\",\n \"name\": \"Designation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Grade\",\n \"name\": \"Employee Grade\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Group\",\n \"name\": \"Employee Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Health Insurance\",\n \"name\": \"Employee Health Insurance\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Employee Lifecycle", - "links": "[\n {\n \"dependencies\": [\n \"Job Applicant\"\n ],\n \"label\": \"Employee Onboarding\",\n \"name\": \"Employee Onboarding\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Skill Map\",\n \"name\": \"Employee Skill Map\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Promotion\",\n \"name\": \"Employee Promotion\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Transfer\",\n \"name\": \"Employee Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation\",\n \"name\": \"Employee Separation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Onboarding Template\",\n \"name\": \"Employee Onboarding Template\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Separation Template\",\n \"name\": \"Employee Separation Template\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Shift Management", - "links": "[\n {\n \"label\": \"Shift Type\",\n \"name\": \"Shift Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Request\",\n \"name\": \"Shift Request\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shift Assignment\",\n \"name\": \"Shift Assignment\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "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 \"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, - "label": "Payroll", - "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Attendance", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance\",\n \"name\": \"Attendance\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Attendance Request\",\n \"name\": \"Attendance Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Upload Attendance\",\n \"name\": \"Upload Attendance\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Checkin\",\n \"name\": \"Employee Checkin\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Attendance\"\n ],\n \"doctype\": \"Attendance\",\n \"is_query_report\": true,\n \"label\": \"Monthly Attendance Sheet\",\n \"name\": \"Monthly Attendance Sheet\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Expense Claims", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Expense Claim\",\n \"name\": \"Expense Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Advance\",\n \"name\": \"Employee Advance\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"label\": \"HR Settings\",\n \"name\": \"HR Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Daily Work Summary Group\",\n \"name\": \"Daily Work Summary Group\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Team Updates\",\n \"name\": \"team-updates\",\n \"type\": \"page\"\n }\n]" - }, - { - "hidden": 0, - "label": "Fleet Management", - "links": "[\n {\n \"label\": \"Vehicle\",\n \"name\": \"Vehicle\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Vehicle Log\",\n \"name\": \"Vehicle Log\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Vehicle\"\n ],\n \"doctype\": \"Vehicle\",\n \"is_query_report\": true,\n \"label\": \"Vehicle Expenses\",\n \"name\": \"Vehicle Expenses\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Recruitment", - "links": "[\n {\n \"label\": \"Job Opening\",\n \"name\": \"Job Opening\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Applicant\",\n \"name\": \"Job Applicant\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Offer\",\n \"name\": \"Job Offer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Staffing Plan\",\n \"name\": \"Staffing Plan\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Loans", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Training", - "links": "[\n {\n \"label\": \"Training Program\",\n \"name\": \"Training Program\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Event\",\n \"name\": \"Training Event\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Result\",\n \"name\": \"Training Result\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Training Feedback\",\n \"name\": \"Training Feedback\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employee Birthday\",\n \"name\": \"Employee Birthday\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"doctype\": \"Employee\",\n \"is_query_report\": true,\n \"label\": \"Employees working on a holiday\",\n \"name\": \"Employees working on a holiday\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Performance", - "links": "[\n {\n \"label\": \"Appraisal\",\n \"name\": \"Appraisal\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appraisal Template\",\n \"name\": \"Appraisal Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Rule\",\n \"name\": \"Energy Point Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Log\",\n \"name\": \"Energy Point Log\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Employee Tax and Benefits", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\",\n \"Payroll Period\"\n ],\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -971,7 +894,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:33:58.806820", + "modified": "2020-12-01 13:38:38.941001", "modified_by": "Administrator", "module": "HR", "name": "HR", diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 57b47288c7..6ef1737e1a 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -1,31 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Loan", - "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Loan Processes", - "links": "[\n {\n \"label\": \"Process Loan Security Shortfall\",\n \"name\": \"Process Loan Security Shortfall\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Process Loan Interest Accrual\",\n \"name\": \"Process Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Disbursement and Repayment", - "links": "[\n {\n \"label\": \"Loan Disbursement\",\n \"name\": \"Loan Disbursement\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Repayment\",\n \"name\": \"Loan Repayment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Write Off\",\n \"name\": \"Loan Write Off\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Interest Accrual\",\n \"name\": \"Loan Interest Accrual\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Loan Security", - "links": "[\n {\n \"label\": \"Loan Security Type\",\n \"name\": \"Loan Security Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Price\",\n \"name\": \"Loan Security Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security\",\n \"name\": \"Loan Security\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Pledge\",\n \"name\": \"Loan Security Pledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Unpledge\",\n \"name\": \"Loan Security Unpledge\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Security Shortfall\",\n \"name\": \"Loan Security Shortfall\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "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", "charts": [], "creation": "2020-03-12 16:35:55.299820", @@ -246,7 +219,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:02.390976", + "modified": "2020-12-01 13:38:36.597212", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 323f5bfc3d..e856a987e3 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -1,31 +1,4 @@ { - "cards": [ - { - "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]" - }, - { - "hidden": 0, - "label": "Bill of Materials", - "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 ],\n \"description\": \"Bill of Materials (BOM)\",\n \"label\": \"Bill of Materials\",\n \"name\": \"BOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Where manufacturing operations are carried.\",\n \"label\": \"Workstation\",\n \"name\": \"Workstation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Details of the operations carried out.\",\n \"label\": \"Operation\",\n \"name\": \"Operation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Routing\",\n \"name\": \"Routing\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "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, - "label": "Tools", - "links": "[\n {\n \"description\": \"Replace BOM and update latest price in all BOMs\",\n \"label\": \"BOM Update Tool\",\n \"name\": \"BOM Update Tool\",\n \"type\": \"doctype\"\n },\n {\n \"data_doctype\": \"BOM\",\n \"description\": \"Compare BOMs for changes in Raw Materials and Operations\",\n \"label\": \"BOM Comparison Tool\",\n \"name\": \"bom-comparison-tool\",\n \"type\": \"page\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"description\": \"Global settings for all manufacturing processes.\",\n \"label\": \"Manufacturing Settings\",\n \"name\": \"Manufacturing Settings\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Domains", "charts": [ { @@ -300,7 +273,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:33:58.367406", + "modified": "2020-12-01 13:38:39.365928", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index 1d5292a9c6..be92d60610 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -1,36 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Loan Management", - "links": "[\n {\n \"description\": \"Define various loan types\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Application\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Grant Application", - "links": "[\n {\n \"description\": \"Grant information.\",\n \"label\": \"Grant Application\",\n \"name\": \"Grant Application\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Membership", - "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Membership Details\",\n \"label\": \"Membership\",\n \"name\": \"Membership\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Membership Type Details\",\n \"label\": \"Membership Type\",\n \"name\": \"Membership Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Billing and Gateway Settings\",\n \"label\": \"Membership Settings\",\n \"name\": \"Membership Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Volunteer", - "links": "[\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer Type information.\",\n \"label\": \"Volunteer Type\",\n \"name\": \"Volunteer Type\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Chapter", - "links": "[\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Donor", - "links": "[\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor Type information.\",\n \"label\": \"Donor Type\",\n \"name\": \"Donor Type\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Domains", "charts": [], "creation": "2020-03-02 17:23:47.811421", @@ -218,7 +186,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:33:59.867233", + "modified": "2020-12-01 13:38:38.351409", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index 3b512893b8..137fad2779 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -1,26 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Payroll", - "links": "[\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Taxation", - "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]" - }, - { - "hidden": 0, - "label": "Compensations", - "links": "[\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n \n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments Based On Payment Mode\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments Based On Payment Mode\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments via ECS\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments via ECS\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Income Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Income Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Professional Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Professional Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Provident Fund Deductions\",\n \"is_query_report\": true,\n \"name\": \"Provident Fund Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Payroll Entry\"\n ],\n \"doctype\": \"Payroll Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Remittance\",\n \"name\": \"Bank Remittance\",\n \"type\": \"report\"\n \n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -309,7 +287,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:01.478995", + "modified": "2020-12-01 13:38:37.205628", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index be49ca7b9e..5c8ab993f3 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -1,21 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Projects", - "links": "[\n {\n \"description\": \"Project master.\",\n \"label\": \"Project\",\n \"name\": \"Project\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Project activity / task.\",\n \"label\": \"Task\",\n \"name\": \"Task\",\n \"onboard\": 1,\n \"route\": \"#List/Task\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make project from a template.\",\n \"label\": \"Project Template\",\n \"name\": \"Project Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define Project type.\",\n \"label\": \"Project Type\",\n \"name\": \"Project Type\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"description\": \"Project Update.\",\n \"label\": \"Project Update\",\n \"name\": \"Project Update\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Time Tracking", - "links": "[\n {\n \"description\": \"Timesheet for tasks.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Types of activities for Time Logs\",\n \"label\": \"Activity Type\",\n \"name\": \"Activity Type\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Cost of various activities\",\n \"label\": \"Activity Cost\",\n \"name\": \"Activity Cost\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Timesheet\"\n ],\n \"doctype\": \"Timesheet\",\n \"is_query_report\": true,\n \"label\": \"Daily Timesheet Summary\",\n \"name\": \"Daily Timesheet Summary\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project wise Stock Tracking\",\n \"name\": \"Project wise Stock Tracking\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Project\"\n ],\n \"doctype\": \"Project\",\n \"is_query_report\": true,\n \"label\": \"Project Billing Summary\",\n \"name\": \"Project Billing Summary\",\n \"type\": \"report\"\n }\n]" - } - ], "category": "Modules", "charts": [ { @@ -167,7 +150,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:00.578885", + "modified": "2020-12-01 13:38:37.856224", "modified_by": "Administrator", "module": "Projects", "name": "Projects", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 1f2c99855b..aaee9ab857 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -1,26 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Goal and Procedure", - "links": "[\n {\n \"description\": \"Quality Goal.\",\n \"label\": \"Quality Goal\",\n \"name\": \"Quality Goal\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Procedure.\",\n \"label\": \"Quality Procedure\",\n \"name\": \"Quality Procedure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Quality Procedures.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Tree of Procedures\",\n \"name\": \"Quality Procedure\",\n \"route\": \"#Tree/Quality Procedure\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Feedback", - "links": "[\n {\n \"description\": \"Quality Feedback\",\n \"label\": \"Quality Feedback\",\n \"name\": \"Quality Feedback\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Feedback Template\",\n \"label\": \"Quality Feedback Template\",\n \"name\": \"Quality Feedback Template\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Meeting", - "links": "[\n {\n \"description\": \"Quality Meeting\",\n \"label\": \"Quality Meeting\",\n \"name\": \"Quality Meeting\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Review and Action", - "links": "[\n {\n \"description\": \"Non Conformance\",\n \"label\": \"Non Conformance\",\n \"name\": \"Non Conformance\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-03-02 15:49:28.632014", @@ -154,7 +132,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:04.570323", + "modified": "2020-12-01 13:38:35.120213", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index 1d220bd3ab..e9beef5f91 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -1,21 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Settings & Configurations", - "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Loyalty Program", - "links": "[\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Opening & Closing", - "links": "[\n {\n \"label\": \"POS Opening Entry\",\n \"name\": \"POS Opening Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Closing Entry\",\n \"name\": \"POS Closing Entry\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Domains", "charts": [], "creation": "2020-03-02 17:18:32.505616", @@ -112,7 +95,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:02.171186", + "modified": "2020-12-01 13:38:36.758038", "modified_by": "Administrator", "module": "Selling", "name": "Retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 776cb3b62a..0bbf17fcae 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -1,31 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Selling", - "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 \"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": "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": "Key Reports", - "links": "[\n {\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 \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\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 \"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 {\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 \"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\": \"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]" - }, - { - "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 \"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 \"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 \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\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 {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Territory Target Variance Based On Item Group\",\n \"name\": \"Territory Target Variance Based On Item Group\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person Target Variance Based On Item Group\",\n \"name\": \"Sales Person Target Variance Based On Item Group\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Partner Target Variance Based On Item Group\",\n \"name\": \"Sales Partner Target Variance based on Item Group\",\n \"type\": \"report\"\n }\n \n]" - } - ], "category": "Modules", "charts": [ { @@ -542,7 +515,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:03.259945", + "modified": "2020-12-01 13:38:35.971277", "modified_by": "Administrator", "module": "Selling", "name": "Selling", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index 0234a3bfb1..a2224515e5 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -1,5 +1,4 @@ { - "cards": [], "category": "Modules", "charts": [], "creation": "2020-03-12 14:47:51.166455", @@ -15,7 +14,7 @@ "is_standard": 1, "label": "ERPNext Settings", "links": [], - "modified": "2020-12-01 12:34:00.741011", + "modified": "2020-12-01 13:38:37.759596", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/desk_page/home/home.json index 702a78c087..fa164d3a03 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -1,51 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Healthcare", - "links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Agriculture", - "links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Education", - "links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Non Profit", - "links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Stock", - "links": "[\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\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\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Human Resources", - "links": "[\n {\n \"label\": \"Employee\",\n \"name\": \"Employee\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"hide_count\": true,\n \"label\": \"Employee Attendance Tool\",\n \"name\": \"Employee Attendance Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "CRM", - "links": "[\n {\n \"description\": \"Database of potential customers.\",\n \"label\": \"Lead\",\n \"name\": \"Lead\",\n \"onboard\": 1,\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 \"onboard\": 1,\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 \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Accounting", - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\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\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create Opening Sales and Purchase Invoices\",\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Data Import and Settings", - "links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Import Chart of Accounts from CSV / Excel files\",\n \"label\": \"Chart of Accounts Importer\",\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Letter Heads for print templates.\",\n \"label\": \"Letter Head\",\n \"name\": \"Letter Head\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-01-23 13:46:38.833076", @@ -464,7 +417,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:00.132264", + "modified": "2020-12-01 13:38:38.131999", "modified_by": "Administrator", "module": "Setup", "name": "Home", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 2713569f4d..fbf8326088 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -1,46 +1,4 @@ { - "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 {\n \"label\": \"Customs Tariff Number\",\n \"name\": \"Customs Tariff Number\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Stock Transactions", - "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, - "label": "Stock Reports", - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Ledger\",\n \"name\": \"Stock Ledger\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Balance\",\n \"name\": \"Stock Balance\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Projected Qty\",\n \"name\": \"Stock Projected Qty\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Summary\",\n \"name\": \"stock-balance\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Ageing\",\n \"name\": \"Stock Ageing\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Price Stock\",\n \"name\": \"Item Price Stock\",\n \"type\": \"report\"\n }\n]" - }, - { - "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\": \"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 {\n \"label\": \"UOM Conversion Factor\",\n \"name\": \"UOM Conversion Factor\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Serial No and Batch", - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Batch\",\n \"name\": \"Batch\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Installation Note\",\n \"name\": \"Installation Note\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Service Contract Expiry\",\n \"name\": \"Serial No Service Contract Expiry\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Status\",\n \"name\": \"Serial No Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Serial No\"\n ],\n \"doctype\": \"Serial No\",\n \"label\": \"Serial No Warranty Expiry\",\n \"name\": \"Serial No Warranty Expiry\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Tools", - "links": "[\n {\n \"label\": \"Stock Reconciliation\",\n \"name\": \"Stock Reconciliation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Landed Cost Voucher\",\n \"name\": \"Landed Cost Voucher\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Packing Slip\",\n \"name\": \"Packing Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection\",\n \"name\": \"Quality Inspection\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quality Inspection Template\",\n \"name\": \"Quality Inspection Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Quick Stock Balance\",\n \"name\": \"Quick Stock Balance\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "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 \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Stock Qty vs Serial No Count\",\n \"name\": \"Stock Qty vs Serial No Count\",\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 \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\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, - "label": "Other Reports", - "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": [ @@ -697,7 +655,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:02.711315", + "modified": "2020-12-01 13:38:36.282890", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index e760ad6bf3..20e60c062b 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -1,36 +1,4 @@ { - "cards": [ - { - "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]" - }, - { - "hidden": 0, - "label": "Maintenance", - "links": "[\n {\n \"label\": \"Maintenance Schedule\",\n \"name\": \"Maintenance Schedule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Maintenance Visit\",\n \"name\": \"Maintenance Visit\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Warranty", - "links": "[\n {\n \"description\": \"Warranty Claim against Serial No.\",\n \"label\": \"Warranty Claim\",\n \"name\": \"Warranty Claim\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Single unit of an Item.\",\n \"label\": \"Serial No\",\n \"name\": \"Serial No\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"label\": \"Support Settings\",\n \"name\": \"Support Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Issue\"\n ],\n \"doctype\": \"Issue\",\n \"is_query_report\": true,\n \"label\": \"First Response Time for Issues\",\n \"name\": \"First Response Time for Issues\",\n \"type\": \"report\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-03-02 15:48:23.224699", @@ -188,7 +156,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:01.698260", + "modified": "2020-12-01 13:38:37.073482", "modified_by": "Administrator", "module": "Support", "name": "Support", diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json index 2a86dfa158..df17303924 100644 --- a/erpnext/utilities/desk_page/utilities/utilities.json +++ b/erpnext/utilities/desk_page/utilities/utilities.json @@ -1,11 +1,4 @@ { - "cards": [ - { - "hidden": 0, - "label": "Video", - "links": "[\n {\n \"description\": \"Video\",\n \"label\": \"Video\",\n \"name\": \"Video\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Video settings\",\n \"label\": \"Video Settings\",\n \"name\": \"Video Settings\",\n \"type\": \"doctype\"\n }\n]" - } - ], "category": "Modules", "charts": [], "creation": "2020-09-10 12:21:22.335307", @@ -47,7 +40,7 @@ "type": "Link" } ], - "modified": "2020-12-01 12:34:02.280074", + "modified": "2020-12-01 13:38:36.711884", "modified_by": "Administrator", "module": "Utilities", "name": "Utilities", From 1ac040a418a4dfd541d7b3a9c46956a043ca276e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Dec 2020 13:50:01 +0530 Subject: [PATCH 200/283] fix: GSTR report --- erpnext/regional/report/gstr_1/gstr_1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 282efe4790..837929709e 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -78,7 +78,7 @@ class Gstr1Report(object): place_of_supply = invoice_details.get("place_of_supply") ecommerce_gstin = invoice_details.get("ecommerce_gstin") - b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin),{ + b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin, inv),{ "place_of_supply": "", "ecommerce_gstin": "", "rate": "", @@ -90,7 +90,7 @@ class Gstr1Report(object): "invoice_value": invoice_details.get("base_grand_total"), }) - row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) + row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin, inv)) row["place_of_supply"] = place_of_supply row["ecommerce_gstin"] = ecommerce_gstin row["rate"] = rate From 2e27f074c3806f63f2ea4f9f70a43ba846c11346 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 1 Dec 2020 18:07:52 +0530 Subject: [PATCH 201/283] feat: reload doctype number card link --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 61aa2eec59..29a7035b83 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -691,6 +691,7 @@ erpnext.patches.v13_0.update_old_loans erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.reload_doctype('Dashboard') +execute:frappe.reload_doc('desk', 'doctype', 'number_card_link') 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 #2020-05-25 From 1c9410e5e835ca81ffc4507a1bb0b3f4e0a801a1 Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Mon, 13 Jul 2020 16:25:09 +0800 Subject: [PATCH 202/283] feat(shipment): Shipment Doctype with Integrations --- .../doctype/letmeship/__init__.py | 0 .../doctype/letmeship/letmeship.js | 8 + .../doctype/letmeship/letmeship.json | 55 ++ .../doctype/letmeship/letmeship.py | 396 +++++++++ .../doctype/letmeship/test_letmeship.py | 10 + .../doctype/packlink/__init__.py | 0 .../doctype/packlink/packlink.js | 8 + .../doctype/packlink/packlink.json | 48 ++ .../doctype/packlink/packlink.py | 237 ++++++ .../doctype/packlink/test_packlink.py | 10 + .../doctype/sendcloud/__init__.py | 0 .../doctype/sendcloud/sendcloud.js | 8 + .../doctype/sendcloud/sendcloud.json | 56 ++ .../doctype/sendcloud/sendcloud.py | 171 ++++ .../doctype/sendcloud/test_sendcloud.py | 10 + erpnext/erpnext_integrations/utils.py | 12 +- .../doctype/delivery_note/delivery_note.js | 12 + .../doctype/delivery_note/delivery_note.py | 53 ++ .../stock/doctype/parcel_service/__init__.py | 0 .../doctype/parcel_service/parcel_service.js | 8 + .../parcel_service/parcel_service.json | 56 ++ .../doctype/parcel_service/parcel_service.py | 10 + .../parcel_service/test_parcel_service.py | 10 + .../doctype/parcel_service_type/__init__.py | 0 .../parcel_service_type.js | 12 + .../parcel_service_type.json | 89 ++ .../parcel_service_type.py | 22 + .../test_parcel_service_type.py | 10 + .../parcel_service_type_alias/__init__.py | 0 .../parcel_service_type_alias.json | 41 + .../parcel_service_type_alias.py | 10 + erpnext/stock/doctype/shipment/__init__.py | 0 erpnext/stock/doctype/shipment/api/utils.py | 67 ++ erpnext/stock/doctype/shipment/shipment.js | 772 ++++++++++++++++++ erpnext/stock/doctype/shipment/shipment.json | 478 +++++++++++ erpnext/stock/doctype/shipment/shipment.py | 300 +++++++ .../stock/doctype/shipment/shipment_list.js | 8 + .../shipment/shipment_service_selector.html | 70 ++ .../stock/doctype/shipment/test_shipment.py | 333 ++++++++ .../shipment_delivery_notes/__init__.py | 0 .../shipment_delivery_notes.json | 41 + .../shipment_delivery_notes.py | 10 + .../__init__.py | 0 .../shipment_notification_subscriptions.json | 40 + .../shipment_notification_subscriptions.py | 10 + .../stock/doctype/shipment_parcel/__init__.py | 0 .../shipment_parcel/shipment_parcel.json | 65 ++ .../shipment_parcel/shipment_parcel.py | 10 + .../shipment_parcel_template/__init__.py | 0 .../shipment_parcel_template.js | 8 + .../shipment_parcel_template.json | 78 ++ .../shipment_parcel_template.py | 10 + .../test_shipment_parcel_template.py | 10 + .../__init__.py | 0 .../shipment_status_update_subscriptions.json | 40 + .../shipment_status_update_subscriptions.py | 10 + 56 files changed, 3721 insertions(+), 1 deletion(-) create mode 100644 erpnext/erpnext_integrations/doctype/letmeship/__init__.py create mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.js create mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.json create mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.py create mode 100644 erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py create mode 100644 erpnext/erpnext_integrations/doctype/packlink/__init__.py create mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.js create mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.json create mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.py create mode 100644 erpnext/erpnext_integrations/doctype/packlink/test_packlink.py create mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/__init__.py create mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js create mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json create mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py create mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py create mode 100644 erpnext/stock/doctype/parcel_service/__init__.py create mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.js create mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.json create mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.py create mode 100644 erpnext/stock/doctype/parcel_service/test_parcel_service.py create mode 100644 erpnext/stock/doctype/parcel_service_type/__init__.py create mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.js create mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.json create mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.py create mode 100644 erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py create mode 100644 erpnext/stock/doctype/parcel_service_type_alias/__init__.py create mode 100644 erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json create mode 100644 erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.py create mode 100644 erpnext/stock/doctype/shipment/__init__.py create mode 100644 erpnext/stock/doctype/shipment/api/utils.py create mode 100644 erpnext/stock/doctype/shipment/shipment.js create mode 100644 erpnext/stock/doctype/shipment/shipment.json create mode 100644 erpnext/stock/doctype/shipment/shipment.py create mode 100644 erpnext/stock/doctype/shipment/shipment_list.js create mode 100644 erpnext/stock/doctype/shipment/shipment_service_selector.html create mode 100644 erpnext/stock/doctype/shipment/test_shipment.py create mode 100644 erpnext/stock/doctype/shipment_delivery_notes/__init__.py create mode 100644 erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json create mode 100644 erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.py create mode 100644 erpnext/stock/doctype/shipment_notification_subscriptions/__init__.py create mode 100644 erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json create mode 100644 erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.py create mode 100644 erpnext/stock/doctype/shipment_parcel/__init__.py create mode 100644 erpnext/stock/doctype/shipment_parcel/shipment_parcel.json create mode 100644 erpnext/stock/doctype/shipment_parcel/shipment_parcel.py create mode 100644 erpnext/stock/doctype/shipment_parcel_template/__init__.py create mode 100644 erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js create mode 100644 erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json create mode 100644 erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py create mode 100644 erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py create mode 100644 erpnext/stock/doctype/shipment_status_update_subscriptions/__init__.py create mode 100644 erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json create mode 100644 erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py diff --git a/erpnext/erpnext_integrations/doctype/letmeship/__init__.py b/erpnext/erpnext_integrations/doctype/letmeship/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.js b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.js new file mode 100644 index 0000000000..1e5e372dff --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.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('LetMeShip', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json new file mode 100644 index 0000000000..4a9a70f251 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2020-07-23 10:55:19.669830", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "api_id", + "api_password" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "api_id", + "fieldtype": "Data", + "label": "API ID", + "read_only_depends_on": "eval:doc.enabled == 0" + }, + { + "fieldname": "api_password", + "fieldtype": "Data", + "label": "API Password", + "read_only_depends_on": "eval:doc.enabled == 0" + } + ], + "issingle": 1, + "links": [], + "modified": "2020-08-05 16:33:44.548230", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "LetMeShip", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py new file mode 100644 index 0000000000..3ad06dbb58 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py @@ -0,0 +1,396 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import requests +import frappe +import json +import re +from frappe import _ +from frappe.model.document import Document +from erpnext.erpnext_integrations.utils import get_tracking_url + +LETMESHIP_PROVIDER = 'LetMeShip' + +class LetMeShip(Document): + pass + +def get_letmeship_available_services(delivery_to_type, pickup_address, + delivery_address, shipment_parcel, description_of_content, pickup_date, + value_of_goods, pickup_contact=None, delivery_contact=None): + # Retrieve rates at LetMeShip from specification stated. + enabled = frappe.db.get_single_value('LetMeShip','enabled') + api_id = frappe.db.get_single_value('LetMeShip','api_id') + api_password = frappe.db.get_single_value('LetMeShip','api_password') + if not enabled or not api_id or not api_password: + return [] + + set_letmeship_specific_fields(pickup_contact, delivery_contact) + + # LetMeShip have limit of 30 characters for Company field + if len(pickup_address.address_title) > 30: + pickup_address.address_title = pickup_address.address_title[:30] + if len(delivery_address.address_title) > 30: + delivery_address.address_title = delivery_address.address_title[:30] + parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content) + + url = 'https://api.letmeship.com/v1/available' + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Origin': 'string' + } + payload = {'pickupInfo': { + 'address': { + 'countryCode': pickup_address.country_code, + 'zip': pickup_address.pincode, + 'city': pickup_address.city, + 'street': pickup_address.address_line1, + 'addressInfo1': pickup_address.address_line2, + 'houseNo': '', + }, + 'company': pickup_address.address_title, + 'person': { + 'title': pickup_contact.title, + 'firstname': pickup_contact.first_name, + 'lastname': pickup_contact.last_name + }, + 'phone': { + 'phoneNumber': pickup_contact.phone, + 'phoneNumberPrefix': pickup_contact.phone_prefix + }, + 'email': pickup_contact.email, + }, 'deliveryInfo': { + 'address': { + 'countryCode': delivery_address.country_code, + 'zip': delivery_address.pincode, + 'city': delivery_address.city, + 'street': delivery_address.address_line1, + 'addressInfo1': delivery_address.address_line2, + 'houseNo': '', + }, + 'company': delivery_address.address_title, + 'person': { + 'title': delivery_contact.title, + 'firstname': delivery_contact.first_name, + 'lastname': delivery_contact.last_name + }, + 'phone': { + 'phoneNumber': delivery_contact.phone, + 'phoneNumberPrefix': delivery_contact.phone_prefix + }, + 'email': delivery_contact.email, + }, 'shipmentDetails': { + 'contentDescription': description_of_content, + 'shipmentType': 'PARCEL', + 'shipmentSettings': { + 'saturdayDelivery': False, + 'ddp': False, + 'insurance': False, + 'pickupOrder': False, + 'pickupTailLift': False, + 'deliveryTailLift': False, + 'holidayDelivery': False, + }, + 'goodsValue': value_of_goods, + 'parcelList': parcel_list, + 'pickupInterval': {'date': pickup_date}, + }} + try: + available_services = [] + response_data = requests.post( + url=url, + auth=(api_id, api_password), + headers=headers, + data=json.dumps(payload) + ) + response_data = json.loads(response_data.text) + if 'serviceList' in response_data: + for response in response_data['serviceList']: + available_service = frappe._dict() + basic_info = response['baseServiceDetails'] + price_info = basic_info['priceInfo'] + available_service.service_provider = LETMESHIP_PROVIDER + available_service.id = basic_info['id'] + available_service.carrier = basic_info['carrier'] + available_service.carrier_name = basic_info['name'] + available_service.service_name = '' + available_service.is_preferred = 0 + available_service.real_weight = price_info['realWeight'] + available_service.total_price = price_info['netPrice'] + available_service.price_info = price_info + available_services.append(available_service) + return available_services + else: + frappe.throw( + _('Error occurred while fetching LetMeShip prices: {0}') + .format(response_data['message']) + ) + except Exception as exc: + frappe.msgprint( + _('Error occurred while fetching LetMeShip Prices: {0}') + .format(str(exc)), + indicator='orange', + alert=True + ) + return [] + + +def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, description_of_content, + pickup_date, value_of_goods, service_info, shipment_notific_email, tracking_notific_email, + pickup_contact=None, delivery_contact=None): + # Create a transaction at LetMeShip + # LetMeShip have limit of 30 characters for Company field + enabled = frappe.db.get_single_value('LetMeShip','enabled') + api_id = frappe.db.get_single_value('LetMeShip','api_id') + api_password = frappe.db.get_single_value('LetMeShip','api_password') + if not enabled or not api_id or not api_password: + return [] + + set_letmeship_specific_fields(pickup_contact, delivery_contact) + + if len(pickup_address.address_title) > 30: + pickup_address.address_title = pickup_address.address_title[:30] + if len(delivery_address.address_title) > 30: + delivery_address.address_title = delivery_address.address_title[:30] + + parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content) + url = 'https://api.letmeship.com/v1/shipments' + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Origin': 'string' + } + payload = { + 'pickupInfo': { + 'address': { + 'countryCode': pickup_address.country_code, + 'zip': pickup_address.pincode, + 'city': pickup_address.city, + 'street': pickup_address.address_line1, + 'addressInfo1': pickup_address.address_line2, + 'houseNo': '', + }, + 'company': pickup_address.address_title, + 'person': { + 'title': pickup_contact.title, + 'firstname': pickup_contact.first_name, + 'lastname': pickup_contact.last_name + }, + 'phone': { + 'phoneNumber': pickup_contact.phone, + 'phoneNumberPrefix': pickup_contact.phone_prefix + }, + 'email': pickup_contact.email, + }, + 'deliveryInfo': { + 'address': { + 'countryCode': delivery_address.country_code, + 'zip': delivery_address.pincode, + 'city': delivery_address.city, + 'street': delivery_address.address_line1, + 'addressInfo1': delivery_address.address_line2, + 'houseNo': '', + }, + 'company': delivery_address.address_title, + 'person': { + 'title': delivery_contact.title, + 'firstname': delivery_contact.first_name, + 'lastname': delivery_contact.last_name + }, + 'phone': { + 'phoneNumber': delivery_contact.phone, + 'phoneNumberPrefix': delivery_contact.phone_prefix + }, + 'email': delivery_contact.email, + }, + 'service': { + 'baseServiceDetails': { + 'id': service_info['id'], + 'name': service_info['service_name'], + 'carrier': service_info['carrier'], + 'priceInfo': service_info['price_info'], + }, + 'supportedExWorkType': [], + 'messages': [''], + 'description': '', + 'serviceInfo': '', + }, + 'shipmentDetails': { + 'contentDescription': description_of_content, + 'shipmentType': 'PARCEL', + 'shipmentSettings': { + 'saturdayDelivery': False, + 'ddp': False, + 'insurance': False, + 'pickupOrder': False, + 'pickupTailLift': False, + 'deliveryTailLift': False, + 'holidayDelivery': False, + }, + 'goodsValue': value_of_goods, + 'parcelList': parcel_list, + 'pickupInterval': { + 'date': pickup_date + }, + 'contentDescription': description_of_content, + }, + 'shipmentNotification': { + 'trackingNotification': { + 'deliveryNotification': True, + 'problemNotification': True, + 'emails': [tracking_notific_email], + 'notificationText': '', + }, + 'recipientNotification': { + 'notificationText': '', + 'emails': [ shipment_notific_email ] + } + }, + 'labelEmail': True, + } + try: + response_data = requests.post( + url=url, + auth=(api_id, api_password), + headers=headers, + data=json.dumps(payload) + ) + response_data = json.loads(response_data.text) + if 'shipmentId' in response_data: + shipment_amount = response_data['service']['priceInfo']['totalPrice'] + awb_number = '' + url = 'https://api.letmeship.com/v1/shipments/{id}'.format(id=response_data['shipmentId']) + tracking_response = requests.get(url, auth=(api_id, api_password),headers=headers) + tracking_response_data = json.loads(tracking_response.text) + if 'trackingData' in tracking_response_data: + for parcel in tracking_response_data['trackingData']['parcelList']: + if 'awbNumber' in parcel: + awb_number = parcel['awbNumber'] + return { + 'service_provider': LETMESHIP_PROVIDER, + 'shipment_id': response_data['shipmentId'], + 'carrier': service_info['carrier'], + 'carrier_service': service_info['service_name'], + 'shipment_amount': shipment_amount, + 'awb_number': awb_number, + } + elif 'message' in response_data: + frappe.throw( + _('Error occurred while creating Shipment: {0}') + .format(response_data['message']) + ) + except Exception as exc: + frappe.msgprint( + _('Error occurred while creating Shipment: {0}') + .format(str(exc)), + indicator='orange', + alert=True + ) + + +def get_letmeship_label(shipment_id): + # Retrieve shipment label from LetMeShip + api_id = frappe.db.get_single_value('LetMeShip','api_id') + api_password = frappe.db.get_single_value('LetMeShip','api_password') + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Origin': 'string' + } + url = 'https://api.letmeship.com/v1/shipments/{id}/documents?types=LABEL'\ + .format(id=shipment_id) + shipment_label_response = requests.get( + url, + auth=(api_id,api_password), + headers=headers + ) + shipment_label_response_data = json.loads(shipment_label_response.text) + if 'documents' in shipment_label_response_data: + for label in shipment_label_response_data['documents']: + if 'data' in label: + return json.dumps(label['data']) + else: + frappe.throw( + _('Error occurred while printing Shipment: {0}') + .format(shipment_label_response_data['message']) + ) + + +def get_letmeship_tracking_data(shipment_id): + # return letmeship tracking data + api_id = frappe.db.get_single_value('LetMeShip','api_id') + api_password = frappe.db.get_single_value('LetMeShip','api_password') + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Origin': 'string' + } + try: + url = 'https://api.letmeship.com/v1/tracking?shipmentid={id}'.format(id=shipment_id) + tracking_data_response = requests.get( + url, + auth=(api_id, api_password), + headers=headers + ) + tracking_data = json.loads(tracking_data_response.text) + if 'awbNumber' in tracking_data: + tracking_status = 'In Progress' + if tracking_data['lmsTrackingStatus'].startswith('DELIVERED'): + tracking_status = 'Delivered' + if tracking_data['lmsTrackingStatus'] == 'RETURNED': + tracking_status = 'Returned' + if tracking_data['lmsTrackingStatus'] == 'LOST': + tracking_status = 'Lost' + tracking_url = get_tracking_url( + carrier=tracking_data['carrier'], + tracking_number=tracking_data['awbNumber'] + ) + return { + 'awb_number': tracking_data['awbNumber'], + 'tracking_status': tracking_status, + 'tracking_status_info': tracking_data['lmsTrackingStatus'], + 'tracking_url': tracking_url, + } + elif 'message' in tracking_data: + frappe.throw( + _('Error occurred while updating Shipment: {0}') + .format(tracking_data['message']) + ) + except Exception as exc: + frappe.msgprint( + _('Error occurred while updating Shipment: {0}') + .format(str(exc)), + indicator='orange', + alert=True + ) + + +def get_parcel_list(shipment_parcel, description_of_content): + parcel_list = [] + for parcel in shipment_parcel: + formatted_parcel = {} + formatted_parcel['height'] = parcel.get('height') + formatted_parcel['width'] = parcel.get('width') + formatted_parcel['length'] = parcel.get('length') + formatted_parcel['weight'] = parcel.get('weight') + formatted_parcel['quantity'] = parcel.get('count') + formatted_parcel['contentDescription'] = description_of_content + parcel_list.append(formatted_parcel) + return parcel_list + +def set_letmeship_specific_fields(pickup_contact, delivery_contact): + pickup_contact.phone_prefix = pickup_contact.phone[:3] + pickup_contact.phone = re.sub('[^A-Za-z0-9]+', '', pickup_contact.phone[3:]) + + pickup_contact.title = 'MS' + if pickup_contact.gender == 'Male': + pickup_contact.title = 'MR' + + delivery_contact.phone_prefix = delivery_contact.phone[:3] + delivery_contact.phone = re.sub('[^A-Za-z0-9]+', '', delivery_contact.phone[3:]) + + delivery_contact.title = 'MS' + if delivery_contact.gender == 'Male': + delivery_contact.title = 'MR' \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py b/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py new file mode 100644 index 0000000000..3439e4fd72 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.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 TestLetMeShip(unittest.TestCase): + pass diff --git a/erpnext/erpnext_integrations/doctype/packlink/__init__.py b/erpnext/erpnext_integrations/doctype/packlink/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.js b/erpnext/erpnext_integrations/doctype/packlink/packlink.js new file mode 100644 index 0000000000..da864584f6 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/packlink/packlink.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('Packlink', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.json b/erpnext/erpnext_integrations/doctype/packlink/packlink.json new file mode 100644 index 0000000000..a56595e9a1 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/packlink/packlink.json @@ -0,0 +1,48 @@ +{ + "actions": [], + "creation": "2020-07-22 10:45:17.672439", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "api_key" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key", + "read_only_depends_on": "eval:doc.enabled == 0" + } + ], + "issingle": 1, + "links": [], + "modified": "2020-08-05 16:33:59.720980", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "Packlink", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.py b/erpnext/erpnext_integrations/doctype/packlink/packlink.py new file mode 100644 index 0000000000..7fdb053cf8 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/packlink/packlink.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import json +import frappe +import requests +from frappe import _ +from frappe.model.document import Document +from erpnext.erpnext_integrations.utils import get_tracking_url + +PACKLINK_PROVIDER = 'Packlink' + +class Packlink(Document): + pass + +def get_packlink_available_services(pickup_address, delivery_address, shipment_parcel,pickup_date): + # Retrieve rates at PackLink from specification stated. + from_zip = pickup_address.pincode + from_country_code = pickup_address.country_code + to_zip = delivery_address.pincode + to_country_code = delivery_address.country_code + shipment_parcel_params = '' + parcel_list = packlink_get_parcel_list(json.loads(shipment_parcel)) + for (index, parcel) in enumerate(parcel_list): + shipment_parcel_params += 'packages[{index}][height]={height}&packages[{index}][length]={length}&packages[{index}][weight]={weight}&packages[{index}][width]={width}&'.format( + index=index, + height=parcel['height'], + length=parcel['length'], + weight=parcel['weight'], + width=parcel['width'] + ) + url = 'https://api.packlink.com/v1/services?from[country]={}&from[zip]={}&to[country]={}&to[zip]={}&{}sortBy=totalPrice&source=PRO'.format( + from_country_code, + from_zip, + to_country_code, + to_zip, + shipment_parcel_params + ) + api_key = frappe.db.get_single_value('Packlink', 'api_key') + enabled = frappe.db.get_single_value('Packlink', 'enabled') + if not api_key or not enabled: + return [] + try: + responses = requests.get(url, headers={'Authorization': api_key}) + responses_dict = json.loads(responses.text) + # If an error occured on the api. Show the error message + if 'messages' in responses_dict: + frappe.msgprint( + _('Packlink: {0}' + .format(str(responses_dict['messages'][0]['message'])) + ), + indicator='orange', + alert=True + ) + available_services = [] + for response in responses_dict: + if parse_pickup_date(pickup_date) \ + in response['available_dates'].keys(): + available_service = frappe._dict() + available_service.service_provider = PACKLINK_PROVIDER + available_service.carrier = response['carrier_name'] + available_service.carrier_name = response['name'] + available_service.service_name = '' + available_service.is_preferred = 0 + available_service.total_price = response['price']['base_price'] + available_service.actual_price = response['price']['total_price'] + available_service.service_id = response['id'] + available_service.available_dates = response['available_dates'] + available_services.append(available_service) + + return available_services + except Exception as exc: + frappe.msgprint( + _('Error occurred on Packlink: {0}') + .format(str(exc)), indicator='orange', + alert=True + ) + return [] + + +def create_packlink_shipment(pickup_address, delivery_address, shipment_parcel, + description_of_content, pickup_date, value_of_goods, pickup_contact, + delivery_contact, service_info): + # Create a transaction at PackLink + enabled = frappe.db.get_single_value('Packlink', 'enabled') + if not enabled: + frappe.throw(_('Packlink integration is not enabled')) + api_key = frappe.db.get_single_value('Packlink', 'api_key') + from_country_code = pickup_address.country_code + to_country_code = delivery_address.country_code + data = { + 'additional_data': { + 'postal_zone_id_from': '', + 'postal_zone_name_from': pickup_address.country, + 'postal_zone_id_to': '', + 'postal_zone_name_to': delivery_address.country, + }, + 'collection_date': parse_pickup_date(pickup_date), + 'collection_time': '', + 'content': description_of_content, + 'contentvalue': value_of_goods, + 'content_second_hand': False, + 'from': { + 'city': pickup_address.city, + 'company': pickup_address.address_title, + 'country': from_country_code, + 'email': pickup_contact.email, + 'name': pickup_contact.first_name, + 'phone': pickup_contact.phone, + 'state': pickup_address.country, + 'street1': pickup_address.address_line1, + 'street2': pickup_address.address_line2, + 'surname': pickup_contact.last_name, + 'zip_code': pickup_address.pincode, + }, + 'insurance': {'amount': 0, 'insurance_selected': False}, + 'price': {}, + 'packages': packlink_get_parcel_list(json.loads(shipment_parcel)), + 'service_id': service_info['service_id'], + 'to': { + 'city': delivery_address.city, + 'company': delivery_address.address_title, + 'country': to_country_code, + 'email': delivery_contact.email, + 'name': delivery_contact.first_name, + 'phone': delivery_contact.phone, + 'state': delivery_address.country, + 'street1': delivery_address.address_line1, + 'street2': delivery_address.address_line2, + 'surname': delivery_contact.last_name, + 'zip_code': delivery_address.pincode, + }, + } + + url = 'https://api.packlink.com/v1/shipments' + headers = { + 'Authorization': api_key, + 'Content-Type': 'application/json' + } + try: + response_data = requests.post(url, json=data, headers=headers) + response_data = json.loads(response_data.text) + if 'reference' in response_data: + return { + 'service_provider': PACKLINK_PROVIDER, + 'shipment_id': response_data['reference'], + 'carrier': service_info['carrier'], + 'carrier_service': service_info['service_name'], + 'shipment_amount': service_info['actual_price'], + 'awb_number': '', + } + except Exception as exc: + frappe.msgprint( + _('Error occurred while creating Shipment: {0}') + .format(str(exc)), + indicator='orange', + alert=True + ) + + +def get_packlink_label(shipment_id): + # Retrieve shipment label from PackLink + enabled = frappe.db.get_single_value('Packlink', 'enabled') + if not enabled: + frappe.throw(_('Packlink integration is not enabled')) + api_key = frappe.db.get_single_value('Packlink', 'api_key') + headers = { + 'Authorization': api_key, + 'Content-Type': 'application/json' + } + shipment_label_response = requests.get( + 'https://api.packlink.com/v1/shipments/{id}/labels'.format(id=shipment_id), + headers=headers + ) + shipment_label = json.loads(shipment_label_response.text) + if shipment_label: + return shipment_label + else: + frappe.msgprint(_('Shipment ID not found')) + + +def get_packlink_tracking_data(shipment_id): + # Get Packlink Tracking Info + enabled = frappe.db.get_single_value('Packlink', 'enabled') + if not enabled: + frappe.throw(_('Packlink integration is not enabled')) + api_key = frappe.db.get_single_value('Packlink', 'api_key') + headers = { + 'Authorization': api_key, + 'Content-Type': 'application/json' + } + try: + url = 'https://api.packlink.com/v1/shipments/{id}'.format(id=shipment_id) + tracking_data_response = requests.get(url, headers=headers) + tracking_data = json.loads(tracking_data_response.text) + if 'trackings' in tracking_data: + tracking_status = 'In Progress' + if tracking_data['state'] == 'DELIVERED': + tracking_status = 'Delivered' + if tracking_data['state'] == 'RETURNED': + tracking_status = 'Returned' + if tracking_data['state'] == 'LOST': + tracking_status = 'Lost' + awb_number = None if not tracking_data['trackings'] else tracking_data['trackings'][0] + tracking_url = get_tracking_url( + carrier=tracking_data['carrier'], + tracking_number=awb_number + ) + return { + 'awb_number': awb_number, + 'tracking_status': tracking_status, + 'tracking_status_info': tracking_data['state'], + 'tracking_url': tracking_url + } + except Exception as exc: + frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( + str(exc)), indicator='orange', alert=True) + return [] + + +def packlink_get_parcel_list(shipment_parcel): + parcel_list = [] + for parcel in shipment_parcel: + for count in range(parcel.get('count')): + formatted_parcel = {} + formatted_parcel['height'] = parcel.get('height') + formatted_parcel['width'] = parcel.get('width') + formatted_parcel['length'] = parcel.get('length') + formatted_parcel['weight'] = parcel.get('weight') + parcel_list.append(formatted_parcel) + return parcel_list + + +def parse_pickup_date(pickup_date): + return pickup_date.replace('-', '/') \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/packlink/test_packlink.py b/erpnext/erpnext_integrations/doctype/packlink/test_packlink.py new file mode 100644 index 0000000000..106ae51f7c --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/packlink/test_packlink.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 TestPacklink(unittest.TestCase): + pass diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/__init__.py b/erpnext/erpnext_integrations/doctype/sendcloud/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js new file mode 100644 index 0000000000..3b85236863 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.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('SendCloud', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json new file mode 100644 index 0000000000..dab54cba6c --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json @@ -0,0 +1,56 @@ +{ + "actions": [], + "creation": "2020-08-18 09:48:50.836233", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "api_key", + "api_secret" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key", + "read_only_depends_on": "eval:doc.enabled == 0" + }, + { + "fieldname": "api_secret", + "fieldtype": "Data", + "label": "API Secret", + "read_only_depends_on": "eval:doc.enabled == 0" + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2020-08-18 09:48:50.836233", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "SendCloud", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py new file mode 100644 index 0000000000..85c94388dc --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py @@ -0,0 +1,171 @@ +# -*- 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 requests +import frappe +import json +from frappe import _ +from frappe.model.document import Document + +SENDCLOUD_PROVIDER = 'SendCloud' + +class SendCloud(Document): + pass + +def get_sendcloud_available_services(delivery_address, shipment_parcel): + # Retrieve rates at SendCloud from specification stated. + enabled = frappe.db.get_single_value('SendCloud', 'enabled') + api_key = frappe.db.get_single_value('SendCloud', 'api_key') + api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + if not enabled or not api_key or not api_secret: + return [] + + try: + url = 'https://panel.sendcloud.sc/api/v2/shipping_methods' + responses = requests.get(url, auth=(api_key, api_secret)) + responses_dict = json.loads(responses.text) + + available_services = [] + for service in responses_dict['shipping_methods']: + for country in service['countries']: + if country['iso_2'] == delivery_address.country_code: + available_service = frappe._dict() + available_service.service_provider = 'SendCloud' + available_service.carrier = service['carrier'] + available_service.service_name = service['name'] + available_service.total_price = total_parcel_price(country['price'], json.loads(shipment_parcel)) + available_service.service_id = service['id'] + available_services.append(available_service) + return available_services + except Exception as exc: + frappe.msgprint(_('Error occurred on SendCloud: {0}').format( + str(exc)), indicator='orange', alert=True) + +def create_sendcloud_shipment( + shipment, + delivery_address, + delivery_contact, + service_info, + shipment_parcel, + description_of_content, + value_of_goods +): + # Create a transaction at SendCloud + enabled = frappe.db.get_single_value('SendCloud', 'enabled') + api_key = frappe.db.get_single_value('SendCloud', 'api_key') + api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + if not enabled or not api_key or not api_secret: + return [] + + parcels = [] + for i, parcel in enumerate(json.loads(shipment_parcel), start=1): + parcel_data = { + 'name': "{} {}".format(delivery_contact.first_name, delivery_contact.last_name), + 'company_name': delivery_address.address_title, + 'address': delivery_address.address_line1, + 'address_2': delivery_address.address_line2 or '', + 'city': delivery_address.city, + 'postal_code': delivery_address.pincode, + 'telephone': delivery_contact.phone, + 'request_label': True, + 'email': delivery_contact.email, + 'data': [], + 'country': delivery_address.country_code, + 'shipment': { + 'id': service_info['service_id'] + }, + 'order_number': "{}-{}".format(shipment, i), + 'external_reference': "{}-{}".format(shipment, i), + 'weight': parcel.get('weight'), + 'parcel_items': get_parcel_items(parcel, description_of_content, value_of_goods) + } + parcels.append(parcel_data) + data = { + 'parcels': parcels + } + try: + url = 'https://panel.sendcloud.sc/api/v2/parcels?errors=verbose' + response_data = requests.post(url, json=data, auth=(api_key, api_secret)) + response_data = json.loads(response_data.text) + if 'failed_parcels' in response_data: + frappe.msgprint(_('Error occurred while creating Shipment: {0}' + ).format(response_data['failed_parcels'][0]['errors']), indicator='orange', + alert=True) + else: + shipment_id = ', '.join([str(x['id']) for x in response_data['parcels']]) + awb_number = ', '.join([str(x['tracking_number']) for x in response_data['parcels']]) + return { + 'service_provider': 'SendCloud', + 'shipment_id': shipment_id, + 'carrier': service_info['carrier'], + 'carrier_service': service_info['service_name'], + 'shipment_amount': service_info['total_price'], + 'awb_number': awb_number + } + except Exception as exc: + frappe.msgprint(_('Error occurred while creating Shipment: {0}').format( + str(exc)), indicator='orange', alert=True) + +def get_sendcloud_label(shipment_id): + # Retrieve shipment label from SendCloud + api_key = frappe.db.get_single_value('SendCloud', 'api_key') + api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + shipment_id_list = shipment_id.split(', ') + label_urls = [] + for ship_id in shipment_id_list: + shipment_label_response = \ + requests.get('https://panel.sendcloud.sc/api/v2/labels/{id}'.format(id=ship_id), auth=(api_key, api_secret)) + shipment_label = json.loads(shipment_label_response.text) + label_urls.append(shipment_label['label']['label_printer']) + if len(label_urls): + return label_urls + else: + frappe.msgprint(_('Shipment ID not found')) + +def get_sendcloud_tracking_data(shipment_id): + # return SendCloud tracking data + try: + api_key = frappe.db.get_single_value('SendCloud', 'api_key') + api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + shipment_id_list = shipment_id.split(', ') + tracking_url = '' + awb_number = [] + tracking_status = [] + tracking_status_info = [] + for ship_id in shipment_id_list: + tracking_data_response = \ + requests.get('https://panel.sendcloud.sc/api/v2/parcels/{id}'.format(id=ship_id), auth=(api_key, api_secret)) + tracking_data = json.loads(tracking_data_response.text) + tracking_url_template = \ + '{{ _("Click here to Track Shipment") }}
' + tracking_url += frappe.render_template(tracking_url_template, {'tracking_url': tracking_data['parcel']['tracking_url']}) + awb_number.append(tracking_data['parcel']['tracking_number']) + tracking_status.append(tracking_data['parcel']['status']['message']) + tracking_status_info.append(tracking_data['parcel']['status']['message']) + return { + 'awb_number': ', '.join(awb_number), + 'tracking_status': ', '.join(tracking_status), + 'tracking_status_info': ', '.join(tracking_status_info), + 'tracking_url': tracking_url + } + except Exception as exc: + frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( + str(exc)), indicator='orange', alert=True) + +def total_parcel_price(parcel_price, shipment_parcel): + count = 0 + for parcel in shipment_parcel: + count += parcel.get('count') + return parcel_price * count + +def get_parcel_items(parcel, description_of_content, value_of_goods): + parcel_list = [] + formatted_parcel = {} + formatted_parcel['description'] = description_of_content + formatted_parcel['quantity'] = parcel.get('count') + formatted_parcel['weight'] = parcel.get('weight') + formatted_parcel['value'] = value_of_goods + parcel_list.append(formatted_parcel) + return parcel_list \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py b/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py new file mode 100644 index 0000000000..5cbe80e8ac --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.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 TestSendCloud(unittest.TestCase): + pass diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py index e278fd7807..e7ef4c8ebd 100644 --- a/erpnext/erpnext_integrations/utils.py +++ b/erpnext/erpnext_integrations/utils.py @@ -60,4 +60,14 @@ def create_mode_of_payment(gateway, payment_type="General"): "default_account": payment_gateway_account }] }) - mode_of_payment.insert(ignore_permissions=True) \ No newline at end of file + mode_of_payment.insert(ignore_permissions=True) + +def get_tracking_url(carrier, tracking_number): + # Return the formatted Tracking URL. + tracking_url = '' + url_reference = frappe.get_value('Parcel Service', carrier, 'url_reference') + if url_reference: + tracking_url = frappe.render_template(url_reference, {'tracking_number': tracking_number}) + tracking_url_template = '{{ _("Click here to Track Shipment") }}' + tracking_url = frappe.render_template(tracking_url_template, {'tracking_url': tracking_url}) + return tracking_url diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 251a26a592..03921c554e 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -156,6 +156,11 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( } if (!doc.is_return && doc.status!="Closed") { + if(doc.docstatus == 1) { + this.frm.add_custom_button(__('Shipment'), function() { + me.make_shipment() }, __('Create')); + } + if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) this.frm.add_custom_button(__('Installation Note'), function() { me.make_installation_note() }, __('Create')); @@ -220,6 +225,13 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( } }, + make_shipment: function() { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.delivery_note.delivery_note.make_shipment", + frm: this.frm + }) + }, + make_sales_invoice: function() { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d04cf785ab..00a66fa48e 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -569,6 +569,59 @@ def make_packing_slip(source_name, target_doc=None): return doclist +@frappe.whitelist() +def make_shipment(source_name, target_doc=None): + def postprocess(source, target): + user = frappe.db.get_value("User", frappe.session.user, ['email', 'full_name', 'phone', 'mobile_no'], as_dict=1) + target.pickup_contact_email = user.email + pickup_contact_display = '{}'.format(user.full_name) + if user.email: + pickup_contact_display += '
' + user.email + if user.phone: + pickup_contact_display += '
' + user.phone + if user.mobile_no and not user.phone: + pickup_contact_display += '
' + user.mobile_no + target.pickup_contact = pickup_contact_display + + contact = frappe.db.get_value("Contact", source.contact_person, ['email_id', 'phone', 'mobile_no'], as_dict=1) + delivery_contact_display = '{}'.format(source.contact_display) + if contact.email_id: + delivery_contact_display += '
' + contact.email_id + if contact.phone: + delivery_contact_display += '
' + contact.phone + if contact.mobile_no and not contact.phone: + delivery_contact_display += '
' + contact.mobile_no + target.delivery_contact = delivery_contact_display + + doclist = get_mapped_doc("Delivery Note", source_name, { + "Delivery Note": { + "doctype": "Shipment", + "field_map": { + "grand_total": "value_of_goods", + "company": "pickup_company", + "company_address": "pickup_address_name", + "company_address_display": "pickup_address", + "address_display": "delivery_address", + "customer": "delivery_customer", + "shipping_address_name": "delivery_address_name", + "contact_person": "delivery_contact_name", + "contact_email": "delivery_contact_email" + }, + "validation": { + "docstatus": ["=", 1] + } + }, + "Delivery Note Item": { + "doctype": "Shipment Delivery Notes", + "field_map": { + "name": "prevdoc_detail_docname", + "parent": "prevdoc_docname", + "parenttype": "prevdoc_doctype", + } + } + }, target_doc, postprocess) + + return doclist @frappe.whitelist() def make_sales_return(source_name, target_doc=None): diff --git a/erpnext/stock/doctype/parcel_service/__init__.py b/erpnext/stock/doctype/parcel_service/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.js b/erpnext/stock/doctype/parcel_service/parcel_service.js new file mode 100644 index 0000000000..43b8ed5bf8 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service/parcel_service.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('Parcel Service', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.json b/erpnext/stock/doctype/parcel_service/parcel_service.json new file mode 100644 index 0000000000..9960acf4ae --- /dev/null +++ b/erpnext/stock/doctype/parcel_service/parcel_service.json @@ -0,0 +1,56 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:parcel_service_name", + "creation": "2020-07-23 10:35:38.211715", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parcel_service_name", + "parcel_service_code", + "url_reference" + ], + "fields": [ + { + "fieldname": "parcel_service_name", + "fieldtype": "Data", + "label": "Parcel Service Name", + "unique": 1 + }, + { + "fieldname": "parcel_service_code", + "fieldtype": "Data", + "label": "Parcel Service Code" + }, + { + "fieldname": "url_reference", + "fieldtype": "Data", + "label": "URL Reference" + } + ], + "links": [], + "modified": "2020-07-23 10:35:38.211715", + "modified_by": "Administrator", + "module": "Stock", + "name": "Parcel Service", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.py b/erpnext/stock/doctype/parcel_service/parcel_service.py new file mode 100644 index 0000000000..e46ac76ef7 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service/parcel_service.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 ParcelService(Document): + pass diff --git a/erpnext/stock/doctype/parcel_service/test_parcel_service.py b/erpnext/stock/doctype/parcel_service/test_parcel_service.py new file mode 100644 index 0000000000..c2f96d9cb0 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service/test_parcel_service.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 TestParcelService(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/parcel_service_type/__init__.py b/erpnext/stock/doctype/parcel_service_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js new file mode 100644 index 0000000000..31d54536c0 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js @@ -0,0 +1,12 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Parcel Service Type Alias', { + parcel_type_alias: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.parcel_type_alias) { + frappe.model.set_value(cdt, cdn, 'parcel_service', frm.doc.parcel_service); + frm.refresh_field('parcel_service_type_alias'); + } + } +}); diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json new file mode 100644 index 0000000000..3c0c4d5f80 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format: {parcel_service} - {parcel_service_type}", + "creation": "2020-07-23 10:47:43.794083", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parcel_service", + "parcel_service_type", + "description", + "section_break_4", + "parcel_service_type_alias", + "column_break_6", + "section_break_7", + "show_in_preferred_services_list" + ], + "fields": [ + { + "fieldname": "parcel_service", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parcel Service", + "options": "Parcel Service", + "reqd": 1 + }, + { + "fieldname": "parcel_service_type", + "fieldtype": "Data", + "label": "Parcel Service Type", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "parcel_service_type_alias", + "fieldtype": "Table", + "label": "Parcel Service Type Alias", + "options": "Parcel Service Type Alias" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "show_in_preferred_services_list", + "fieldtype": "Check", + "label": "Show in Preferred Services List" + } + ], + "links": [], + "modified": "2020-07-23 10:47:43.794083", + "modified_by": "Administrator", + "module": "Stock", + "name": "Parcel Service Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py new file mode 100644 index 0000000000..b55528c359 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py @@ -0,0 +1,22 @@ +# -*- 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 ParcelServiceType(Document): + pass + +def match_parcel_service_type_alias(parcel_service_type, parcel_service): + # Match and return Parcel Service Type Alias to Parcel Service Type if exists. + if frappe.db.exists('Parcel Service', parcel_service): + matched_parcel_service_type = \ + frappe.db.get_value('Parcel Service Type Alias', { + 'parcel_type_alias': parcel_service_type, + 'parcel_service': parcel_service + }, 'parent') + if matched_parcel_service_type: + parcel_service_type = matched_parcel_service_type + return parcel_service_type diff --git a/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py b/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py new file mode 100644 index 0000000000..e214264acc --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.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 TestParcelServiceType(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/parcel_service_type_alias/__init__.py b/erpnext/stock/doctype/parcel_service_type_alias/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json new file mode 100644 index 0000000000..8e7731e6c1 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "creation": "2020-07-23 10:47:23.626510", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parcel_service", + "parcel_type_alias" + ], + "fields": [ + { + "fieldname": "parcel_service", + "fieldtype": "Link", + "hidden": 1, + "in_list_view": 1, + "label": "Parcel Service", + "options": "Parcel Service", + "read_only": 1 + }, + { + "fieldname": "parcel_type_alias", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parcel Type Alias", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-23 10:47:23.626510", + "modified_by": "Administrator", + "module": "Stock", + "name": "Parcel Service Type Alias", + "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/parcel_service_type_alias/parcel_service_type_alias.py b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.py new file mode 100644 index 0000000000..fd0a7d8b49 --- /dev/null +++ b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.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 ParcelServiceTypeAlias(Document): + pass diff --git a/erpnext/stock/doctype/shipment/__init__.py b/erpnext/stock/doctype/shipment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment/api/utils.py b/erpnext/stock/doctype/shipment/api/utils.py new file mode 100644 index 0000000000..1153933e81 --- /dev/null +++ b/erpnext/stock/doctype/shipment/api/utils.py @@ -0,0 +1,67 @@ +# -*- 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 import _ +import re + +def get_address(address_name): + address = frappe.db.get_value('Address', address_name, [ + 'address_title', + 'address_line1', + 'address_line2', + 'city', + 'pincode', + 'country', + ], as_dict=1) + address.country_code = frappe.db.get_value('Country', address.country, 'code').upper() + if not address.pincode or address.pincode == '': + frappe.throw(_("Postal Code is mandatory to continue.
\ + Please set Postal Code for Address {1}" + ).format(address_name, address_name)) + address.pincode = address.pincode.replace(' ', '') + address.city = address.city.strip() + return address + +def get_contact(contact_name): + contact = frappe.db.get_value('Contact', contact_name, [ + 'first_name', + 'last_name', + 'email_id', + 'phone', + 'mobile_no', + 'gender', + ], as_dict=1) + if not contact.last_name: + frappe.throw(_("Last Name is mandatory to continue.
\ + Please set Last Name for Contact {1}" + ).format(contact_name, contact_name)) + if not contact.phone: + contact.phone = contact.mobile_no + contact.phone_prefix = contact.phone[:3] + contact.phone = re.sub('[^A-Za-z0-9]+', '', contact.phone[3:]) + contact.email = contact.email_id + contact.title = 'MS' + if contact.gender == 'Male': + contact.title = 'MR' + return contact + +def get_company_contact(): + contact = frappe.db.get_value('User', frappe.session.user, [ + 'first_name', + 'last_name', + 'email', + 'phone', + 'mobile_no', + 'gender', + ], as_dict=1) + if not contact.phone: + contact.phone = contact.mobile_no + contact.phone_prefix = contact.phone[:3] + contact.phone = re.sub('[^A-Za-z0-9]+', '', contact.phone[3:]) + contact.title = 'MS' + if contact.gender == 'Male': + contact.title = 'MR' + return contact diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js new file mode 100644 index 0000000000..e9f4484ab1 --- /dev/null +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -0,0 +1,772 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Shipment', { + setup: function(frm) { + if (frm.doc.__islocal) { + frm.trigger('pickup_type'); + } + }, + address_query: function(frm, link_doctype, link_name, is_your_company_address) { + return { + query: 'frappe.contacts.doctype.address.address.address_query', + filters: { + link_doctype: link_doctype, + link_name: link_name, + is_your_company_address: is_your_company_address + } + }; + }, + contact_query: function(frm, link_doctype, link_name) { + return { + query: 'frappe.contacts.doctype.contact.contact.contact_query', + filters: { + link_doctype: link_doctype, + link_name: link_name + } + }; + }, + onload: function(frm) { + frm.set_query("delivery_address_name", () => { + let link_doctype = ''; + let link_name = ''; + let is_your_company_address = 0; + if (frm.doc.delivery_to_type == 'Customer') { + link_doctype = 'Customer'; + link_name = frm.doc.delivery_customer; + } + if (frm.doc.delivery_to_type == 'Supplier') { + link_doctype = 'Supplier'; + link_name = frm.doc.delivery_supplier; + } + if (frm.doc.delivery_to_type == 'Company') { + link_doctype = 'Company'; + link_name = frm.doc.delivery_company; + is_your_company_address = 1; + } + return frm.events.address_query(frm, link_doctype, link_name, is_your_company_address); + }); + frm.set_query("pickup_address_name", () => { + let link_doctype = ''; + let link_name = ''; + let is_your_company_address = 0; + if (frm.doc.pickup_from_type == 'Customer') { + link_doctype = 'Customer'; + link_name = frm.doc.pickup_customer; + } + if (frm.doc.pickup_from_type == 'Supplier') { + link_doctype = 'Supplier'; + link_name = frm.doc.pickup_supplier; + } + if (frm.doc.pickup_from_type == 'Company') { + link_doctype = 'Company'; + link_name = frm.doc.pickup_company; + is_your_company_address = 1; + } + return frm.events.address_query(frm, link_doctype, link_name, is_your_company_address); + }); + frm.set_query("delivery_contact_name", () => { + let link_doctype = ''; + let link_name = ''; + if (frm.doc.delivery_to_type == 'Customer') { + link_doctype = 'Customer'; + link_name = frm.doc.delivery_customer; + } + if (frm.doc.delivery_to_type == 'Supplier') { + link_doctype = 'Supplier'; + link_name = frm.doc.delivery_supplier; + } + if (frm.doc.delivery_to_type == 'Company') { + link_doctype = 'Company'; + link_name = frm.doc.delivery_company; + } + return frm.events.contact_query(frm, link_doctype, link_name); + }); + frm.set_query("pickup_contact_name", () => { + let link_doctype = ''; + let link_name = ''; + if (frm.doc.pickup_from_type == 'Customer') { + link_doctype = 'Customer'; + link_name = frm.doc.pickup_customer; + } + if (frm.doc.pickup_from_type == 'Supplier') { + link_doctype = 'Supplier'; + link_name = frm.doc.pickup_supplier; + } + if (frm.doc.pickup_from_type == 'Company') { + link_doctype = 'Company'; + link_name = frm.doc.pickup_company; + } + return frm.events.contact_query(frm, link_doctype, link_name); + }); + frm.set_query("delivery_note", "shipment_delivery_notes", function() { + let customer = ''; + if (frm.doc.delivery_to_type == "Customer") { + customer = frm.doc.delivery_customer; + } + if (frm.doc.delivery_to_type == "Company") { + customer = frm.doc.delivery_company; + } + if (customer) { + return { + filters: { + customer: customer, + docstatus: 1, + status: ["not in", ["Cancelled"]] + } + }; + } + }); + }, + refresh: function(frm) { + if (frm.doc.docstatus === 1 && !frm.doc.shipment_id) { + frm.add_custom_button(__('Fetch Shipping Rates'), function() { + return frm.events.fetch_shipping_rates(frm); + }); + } + if (frm.doc.shipment_id) { + frm.add_custom_button(__('Print Shipping Label'), function() { + return frm.events.print_shipping_label(frm); + }); + if (frm.doc.tracking_status != 'Delivered') { + frm.add_custom_button(__('Update Tracking'), function() { + return frm.events.update_tracking(frm, frm.doc.service_provider, frm.doc.shipment_id); + }); + } + } + $('div[data-fieldname=pickup_address] > div > .clearfix').hide(); + $('div[data-fieldname=pickup_contact] > div > .clearfix').hide(); + $('div[data-fieldname=delivery_address] > div > .clearfix').hide(); + $('div[data-fieldname=delivery_contact] > div > .clearfix').hide(); + + if (frm.doc.delivery_from_type != 'Company') { + frm.set_df_property("delivery_contact_name", "reqd", 1); + } + if (frm.doc.pickup_from_type != 'Company') { + frm.set_df_property("pickup_contact_name", "reqd", 1); + } + else { + frm.toggle_display("pickup_contact_name", false); + } + }, + before_save: function(frm) { + if (frm.doc.delivery_to_type == 'Company') { + frm.set_value("delivery_to", frm.doc.delivery_company); + } + if (frm.doc.delivery_to_type == 'Customer') { + frm.set_value("delivery_to", frm.doc.delivery_customer); + } + if (frm.doc.delivery_to_type == 'Supplier') { + frm.set_value("delivery_to", frm.doc.delivery_supplier); + } + if (frm.doc.pickup_from_type == 'Company') { + frm.set_value("pickup", frm.doc.pickup_company); + } + if (frm.doc.pickup_from_type == 'Customer') { + frm.set_value("pickup", frm.doc.pickup_customer); + } + if (frm.doc.pickup_from_type == 'Supplier') { + frm.set_value("pickup", frm.doc.pickup_supplier); + } + }, + set_pickup_company_address: function(frm) { + frappe.db.get_value('Address', { + address_title: frm.doc.pickup_company, + is_your_company_address: 1 + }, 'name', (r) => { + frm.set_value("pickup_address_name", r.name); + }); + }, + set_delivery_company_address: function(frm) { + frappe.db.get_value('Address', { + address_title: frm.doc.delivery_company, + is_your_company_address: 1 + }, 'name', (r) => { + frm.set_value("delivery_address_name", r.name); + }); + }, + pickup_from_type: function(frm) { + if (frm.doc.pickup_from_type == 'Company') { + frm.set_value("pickup_company", frappe.defaults.get_default('company')); + frm.set_df_property("pickup_contact_name", "reqd", 0); + frm.set_value("pickup_customer", ''); + frm.set_value("pickup_supplier", ''); + frm.toggle_display("pickup_contact_name", false); + } + else { + frm.set_df_property("pickup_contact_name", "reqd", 1); + frm.toggle_display("pickup_contact_name", true); + frm.trigger('clear_pickup_fields'); + } + if (frm.doc.pickup_from_type == 'Customer') { + frm.set_value("pickup_company", ''); + frm.set_value("pickup_supplier", ''); + } + if (frm.doc.pickup_from_type == 'Supplier') { + frm.set_value("pickup_customer", ''); + frm.set_value("pickup_company", ''); + } + frm.events.remove_notific_child_table(frm, 'shipment_notification_subscriptions', 'Pickup'); + frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscriptions', 'Pickup'); + }, + delivery_to_type: function(frm) { + if (frm.doc.delivery_to_type == 'Company') { + frm.set_value("delivery_company", frappe.defaults.get_default('company')); + frm.set_df_property("delivery_contact_name", "reqd", 0); + frm.set_value("delivery_customer", ''); + frm.set_value("delivery_supplier", ''); + frm.toggle_display("delivery_contact_name", false); + } + else { + frm.set_df_property("delivery_contact_name", "reqd", 1); + frm.toggle_display("delivery_contact_name", true); + frm.trigger('clear_delivery_fields'); + } + if (frm.doc.delivery_to_type == 'Customer') { + frm.set_value("delivery_company", ''); + frm.set_value("delivery_supplier", ''); + } + if (frm.doc.delivery_to_type == 'Supplier') { + frm.set_value("delivery_customer", ''); + frm.set_value("delivery_company", ''); + frm.toggle_display("shipment_delivery_notes", false); + } + else { + frm.toggle_display("shipment_delivery_notes", true); + } + frm.events.remove_notific_child_table(frm, 'shipment_notification_subscriptions', 'Delivery'); + frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscriptions', 'Delivery'); + }, + delivery_address_name: function(frm) { + if (frm.doc.delivery_to_type == 'Company') { + erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', true); + } + else { + erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', false); + } + }, + pickup_address_name: function(frm) { + if (frm.doc.pickup_from_type == 'Company') { + erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', true); + } + else { + erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', false); + } + }, + get_contact_display: function(frm, contact_name, contact_type) { + frappe.call({ + method: "frappe.contacts.doctype.contact.contact.get_contact_details", + args: { contact: contact_name }, + callback: function(r) { + if(r.message) { + if (!(r.message.contact_email && (r.message.contact_phone || r.message.contact_mobile))) { + if (contact_type == 'Delivery') { + frm.set_value('delivery_contact_name', ''); + frm.set_value('delivery_contact', ''); + } + else { + frm.set_value('pickup_contact_name', ''); + frm.set_value('pickup_contact', ''); + } + frappe.throw(__(`Email or Phone/Mobile of the Contact are mandatory to continue.
+ Please set Email/Phone for the contact ${contact_name}`)); + } + let contact_display = r.message.contact_display; + if (r.message.contact_email) { + contact_display += '
' + r.message.contact_email; + } + if (r.message.contact_phone) { + contact_display += '
' + r.message.contact_phone; + } + if (r.message.contact_mobile && !r.message.contact_phone) { + contact_display += '
' + r.message.contact_mobile; + } + if (contact_type == 'Delivery'){ + frm.set_value('delivery_contact', contact_display); + if (r.message.contact_email) { + frm.set_value('delivery_contact_email', r.message.contact_email); + } + } + else { + frm.set_value('pickup_contact', contact_display); + if (r.message.contact_email) { + frm.set_value('pickup_contact_email', r.message.contact_email); + } + } + } + } + }); + }, + delivery_contact_name: function(frm) { + if (frm.doc.delivery_contact_name) { + frm.events.get_contact_display(frm, frm.doc.delivery_contact_name, 'Delivery'); + } + }, + pickup_contact_name: function(frm) { + if (frm.doc.pickup_contact_name) { + frm.events.get_contact_display(frm, frm.doc.pickup_contact_name, 'Pickup'); + } + }, + set_company_contact: function(frm, delivery_type) { + frappe.db.get_value('User', { name: frappe.session.user }, ['full_name', 'last_name', 'email', 'phone', 'mobile_no'], (r) => { + if (!(r.last_name && r.email && (r.phone || r.mobile_no))) { + if (delivery_type == 'Delivery') { + frm.set_value('delivery_company', ''); + frm.set_value('delivery_contact', ''); + } + else { + frm.set_value('pickup_company', ''); + frm.set_value('pickup_contact', ''); + } + frappe.throw(__(`Last Name, Email or Phone/Mobile of the user are mandatory to continue.
+ Please first set Last Name, Email and Phone for the user ${frappe.session.user}`)); + } + let contact_display = r.full_name; + if (r.email) { + contact_display += '
' + r.email; + } + if (r.phone) { + contact_display += '
' + r.phone; + } + if (r.mobile_no && !r.phone) { + contact_display += '
' + r.mobile_no; + } + if (delivery_type == 'Delivery') { + frm.set_value('delivery_contact', contact_display); + if (r.email) { + frm.set_value('delivery_contact_email', r.email); + } + } + else { + frm.set_value('pickup_contact', contact_display); + if (r.email) { + frm.set_value('pickup_contact_email', r.email); + } + } + }); + }, + pickup_company: function(frm) { + if (frm.doc.pickup_from_type == 'Company' && frm.doc.pickup_company) { + frm.trigger('set_pickup_company_address'); + frm.events.set_company_contact(frm, 'Pickup'); + } + }, + delivery_company: function(frm) { + if (frm.doc.delivery_to_type == 'Company' && frm.doc.delivery_company) { + frm.trigger('set_delivery_company_address'); + frm.events.set_company_contact(frm, 'Delivery'); + } + }, + delivery_customer: function(frm) { + frm.trigger('clear_delivery_fields'); + if (frm.doc.delivery_customer) { + frm.events.set_address_name(frm,'Customer',frm.doc.delivery_customer, 'Delivery'); + frm.events.set_contact_name(frm,'Customer',frm.doc.delivery_customer, 'Delivery'); + } + }, + delivery_supplier: function(frm) { + frm.trigger('clear_delivery_fields'); + if (frm.doc.delivery_supplier) { + frm.events.set_address_name(frm,'Supplier',frm.doc.delivery_supplier, 'Delivery'); + frm.events.set_contact_name(frm,'Supplier',frm.doc.delivery_supplier, 'Delivery'); + } + }, + pickup_customer: function(frm) { + frm.trigger('clear_pickup_fields'); + if (frm.doc.pickup_customer) { + frm.events.set_address_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); + frm.events.set_contact_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); + } + }, + pickup_supplier: function(frm) { + frm.trigger('clear_pickup_fields'); + if (frm.doc.pickup_supplier) { + frm.events.set_address_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); + frm.events.set_contact_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); + } + }, + set_address_name: function(frm, ref_doctype, ref_docname, delivery_type) { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.get_address_name", + args: { + ref_doctype: ref_doctype, + docname: ref_docname + }, + callback: function(r) { + if(r.message) { + if (delivery_type == 'Delivery') { + frm.set_value('delivery_address_name', r.message); + } + else { + frm.set_value('pickup_address_name', r.message); + } + } + } + }); + }, + set_contact_name: function(frm, ref_doctype, ref_docname, delivery_type) { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.get_contact_name", + args: { + ref_doctype: ref_doctype, + docname: ref_docname + }, + callback: function(r) { + if(r.message) { + if (delivery_type == 'Delivery') { + frm.set_value('delivery_contact_name', r.message); + } + else { + frm.set_value('pickup_contact_name', r.message); + } + } + } + }); + }, + add_template: function(frm) { + if (frm.doc.parcel_template) { + frappe.model.with_doc("Shipment Parcel Template", frm.doc.parcel_template, () => { + let parcel_template = frappe.model.get_doc("Shipment Parcel Template", frm.doc.parcel_template); + let row = frappe.model.add_child(frm.doc, "Shipment Parcel", "shipment_parcel"); + row.length = parcel_template.length; + row.width = parcel_template.width; + row.height = parcel_template.height; + row.weight = parcel_template.weight; + frm.refresh_fields("shipment_parcel"); + }); + } + }, + pickup_date: function(frm) { + if (frm.doc.pickup_date < frappe.datetime.get_today()) { + frappe.throw(__("Pickup Date cannot be in the past")); + } + if (frm.doc.pickup_date == frappe.datetime.get_today()) { + var pickup_time = frm.events.get_pickup_time(frm); + frm.set_value("pickup_from", pickup_time); + frm.trigger('set_pickup_to_time'); + } + }, + pickup_from: function(frm) { + var pickup_time = frm.events.get_pickup_time(frm); + if (frm.doc.pickup_from && frm.doc.pickup_date == frappe.datetime.get_today()) { + let current_hour = pickup_time.split(':')[0]; + let current_min = pickup_time.split(':')[1]; + let pickup_hour = frm.doc.pickup_from.split(':')[0]; + let pickup_min = frm.doc.pickup_from.split(':')[1]; + if (pickup_hour < current_hour || (pickup_hour == current_hour && pickup_min < current_min)) { + frm.set_value("pickup_from", pickup_time); + frappe.throw(__("Pickup Time cannot be in the past")); + } + } + frm.trigger('set_pickup_to_time'); + }, + get_pickup_time: function() { + let current_hour = new Date().getHours(); + let current_min = new Date().toLocaleString('en-US', {minute: 'numeric'}); + if (current_min < 30) { + current_min = '30'; + } + else { + current_min = '00'; + current_hour = Number(current_hour)+1; + } + if (Number(current_hour) > 19 || Number(current_hour) === 19){ + frappe.throw(__("Today's pickup time is over, please select different date")); + } + current_hour = (current_hour < 10) ? '0' + current_hour : current_hour; + let pickup_time = current_hour +':'+ current_min; + return pickup_time; + }, + set_pickup_to_time: function(frm) { + let pickup_to_hour = Number(frm.doc.pickup_from.split(':')[0])+5; + if (Number(pickup_to_hour) > 19 || Number(pickup_to_hour) === 19){ + pickup_to_hour = 19; + } + let pickup_to_min = frm.doc.pickup_from.split(':')[1]; + let pickup_to = pickup_to_hour +':'+ pickup_to_min; + frm.set_value("pickup_to", pickup_to); + }, + clear_pickup_fields: function(frm) { + frm.set_value("pickup_address_name", ''); + frm.set_value("pickup_contact_name", ''); + frm.set_value("pickup_address", ''); + frm.set_value("pickup_contact", ''); + frm.set_value("pickup_contact_email", ''); + }, + clear_delivery_fields: function(frm) { + frm.set_value("delivery_address_name", ''); + frm.set_value("delivery_contact_name", ''); + frm.set_value("delivery_address", ''); + frm.set_value("delivery_contact", ''); + frm.set_value("delivery_contact_email", ''); + }, + pickup_from_send_shipping_notification: function(frm, cdt, cdn) { + if (frm.doc.pickup_contact_email && frm.doc.pickup_from_send_shipping_notification + && !validate_duplicate(frm, 'shipment_notification_subscriptions', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscriptions", "shipment_notification_subscriptions"); + row.email = frm.doc.pickup_contact_email; + frm.refresh_fields("shipment_notification_subscriptions"); + } + if (!frm.doc.pickup_from_send_shipping_notification) { + frm.events.remove_email_row(frm, 'shipment_notification_subscriptions', frm.doc.pickup_contact_email); + frm.refresh_fields("shipment_notification_subscriptions"); + } + }, + pickup_from_subscribe_to_status_updates: function(frm, cdt, cdn) { + if (frm.doc.pickup_contact_email && frm.doc.pickup_from_subscribe_to_status_updates + && !validate_duplicate(frm, 'shipment_status_update_subscriptions', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscriptions", "shipment_status_update_subscriptions"); + row.email = frm.doc.pickup_contact_email; + frm.refresh_fields("shipment_status_update_subscriptions"); + } + if (!frm.doc.pickup_from_subscribe_to_status_updates) { + frm.events.remove_email_row(frm, 'shipment_status_update_subscriptions', frm.doc.pickup_contact_email); + frm.refresh_fields("shipment_status_update_subscriptions"); + } + }, + delivery_to_send_shipping_notification: function(frm, cdt, cdn) { + if (frm.doc.delivery_contact_email && frm.doc.delivery_to_send_shipping_notification + && !validate_duplicate(frm, 'shipment_notification_subscriptions', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)){ + let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscriptions", "shipment_notification_subscriptions"); + row.email = frm.doc.delivery_contact_email; + frm.refresh_fields("shipment_notification_subscriptions"); + } + if (!frm.doc.delivery_to_send_shipping_notification) { + frm.events.remove_email_row(frm, 'shipment_notification_subscriptions', frm.doc.delivery_contact_email); + frm.refresh_fields("shipment_notification_subscriptions"); + } + }, + delivery_to_subscribe_to_status_updates: function(frm, cdt, cdn) { + if (frm.doc.delivery_contact_email && frm.doc.delivery_to_subscribe_to_status_updates + && !validate_duplicate(frm, 'shipment_status_update_subscriptions', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscriptions", "shipment_status_update_subscriptions"); + row.email = frm.doc.delivery_contact_email; + frm.refresh_fields("shipment_status_update_subscriptions"); + } + if (!frm.doc.delivery_to_subscribe_to_status_updates) { + frm.events.remove_email_row(frm, 'shipment_status_update_subscriptions', frm.doc.delivery_contact_email); + frm.refresh_fields("shipment_status_update_subscriptions"); + } + }, + remove_email_row: function(frm, table, fieldname) { + $.each(frm.doc[table] || [], function(i, detail) { + if(detail.email === fieldname){ + cur_frm.get_field(table).grid.grid_rows[i].remove(); + } + }); + }, + remove_notific_child_table: function(frm, table, delivery_type) { + $.each(frm.doc[table] || [], function(i, detail) { + if (detail.email != frm.doc.pickup_email || detail.email != frm.doc.delivery_email){ + cur_frm.get_field(table).grid.grid_rows[i].remove(); + } + }); + frm.refresh_fields(table); + if (delivery_type == 'Delivery') { + frm.set_value("delivery_to_send_shipping_notification", 0); + frm.set_value("delivery_to_subscribe_to_status_updates", 0); + frm.refresh_fields("delivery_to_send_shipping_notification"); + frm.refresh_fields("delivery_to_subscribe_to_status_updates"); + } + else { + frm.set_value("pickup_from_send_shipping_notification", 0); + frm.set_value("pickup_from_subscribe_to_status_updates", 0); + frm.refresh_fields("pickup_from_send_shipping_notification"); + frm.refresh_fields("pickup_from_subscribe_to_status_updates"); + } + }, + fetch_shipping_rates: function(frm) { + if (!frm.doc.shipment_id) { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.fetch_shipping_rates", + freeze: true, + freeze_message: __("Fetching Shipping Rates"), + args: { + pickup_from_type: frm.doc.pickup_from_type, + delivery_to_type: frm.doc.delivery_to_type, + pickup_address_name: frm.doc.pickup_address_name, + delivery_address_name: frm.doc.delivery_address_name, + shipment_parcel: frm.doc.shipment_parcel, + description_of_content: frm.doc.description_of_content, + pickup_date: frm.doc.pickup_date, + pickup_contact_name: frm.doc.pickup_contact_name, + delivery_contact_name: frm.doc.delivery_contact_name, + value_of_goods: frm.doc.value_of_goods + }, + callback: function(r) { + if (r.message) { + select_from_available_services(frm, r.message); + } + else { + frappe.throw(__("No Shipment Services available")); + } + } + }); + } + else { + frappe.throw(__("Shipment already created")); + } + }, + print_shipping_label: function(frm) { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.print_shipping_label", + freeze: true, + freeze_message: __("Printing Shipping Label"), + args: { + shipment_id: frm.doc.shipment_id, + service_provider: frm.doc.service_provider + }, + callback: function(r) { + if (r.message) { + if (frm.doc.service_provider == "LetMeShip") { + var array = JSON.parse(r.message); + // Uint8Array for unsigned bytes + array = new Uint8Array(array); + const file = new Blob([array], {type: "application/pdf"}); + const file_url = URL.createObjectURL(file); + window.open(file_url); + } + else { + if (Array.isArray(r.message)) { + r.message.forEach(url => window.open(url)); + } else { + window.open(r.message); + } + } + } + } + }); + }, + update_tracking: function(frm, service_provider, shipment_id) { + let delivery_notes = []; + (frm.doc.shipment_delivery_notes || []).forEach((d) => { + delivery_notes.push(d.delivery_note); + }); + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.update_tracking", + freeze: true, + freeze_message: __("Updating Tracking"), + args: { + shipment: frm.doc.name, + shipment_id: shipment_id, + service_provider: service_provider, + delivery_notes: delivery_notes + }, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + } + } + }); + } +}); + +frappe.ui.form.on('Shipment Delivery Notes', { + delivery_note: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.delivery_note) { + let row_index = row.idx - 1; + if(validate_duplicate(frm, 'shipment_delivery_notes', row.delivery_note, row_index)) { + cur_frm.get_field('shipment_delivery_notes').grid.grid_rows[row_index].remove(); + frappe.throw(__(`You have entered duplicate Delivery Notes. Please rectify and try again.`)); + } + } + }, + grand_total: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.grand_total) { + var value_of_goods = parseFloat(frm.doc.value_of_goods)+parseFloat(row.grand_total); + frm.set_value("value_of_goods", Math.round(value_of_goods)); + frm.refresh_fields("value_of_goods"); + } + }, +}); + +var validate_duplicate = function(frm, table, fieldname, index){ + let duplicate = false; + $.each(frm.doc[table], function(i, detail) { + // Email duplicate validation + if(detail.email === fieldname && !(index === i)) { + duplicate = true; + return; + } + + // Delivery Note duplicate validation + if(detail.delivery_note === fieldname && !(index === i)) { + duplicate = true; + return; + } + }); + return duplicate; +}; + +function select_from_available_services(frm, available_services) { + var headers = [ __("Service Provider"), __("Carrier"), __("Carrier’s Service"), __("Price"), "" ]; + cur_frm.render_available_services = function(d, headers, data){ + d.fields_dict.available_services.$wrapper.html( + frappe.render_template('shipment_service_selector', + {'header_columns': headers, 'data': data} + ) + ); + }; + const d = new frappe.ui.Dialog({ + title: __("Select Shipment Service to create Shipment"), + fields: [ + { + fieldtype:'HTML', + fieldname:"available_services", + label: __('Available Services') + } + ] + }); + cur_frm.render_available_services(d, headers, available_services); + let shipment_notific_email = []; + let tracking_notific_email = []; + (frm.doc.shipment_notification_subscriptions || []).forEach((d) => { + if (!d.unsubscribed) { + shipment_notific_email.push(d.email); + } + }); + (frm.doc.shipment_status_update_subscriptions || []).forEach((d) => { + if (!d.unsubscribed) { + tracking_notific_email.push(d.email); + } + }); + let delivery_notes = []; + (frm.doc.shipment_delivery_notes || []).forEach((d) => { + delivery_notes.push(d.delivery_note); + }); + cur_frm.select_row = function(service_data){ + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.create_shipment", + freeze: true, + freeze_message: __("Creating Shipment"), + args: { + shipment: frm.doc.name, + pickup_from_type: frm.doc.pickup_from_type, + delivery_to_type: frm.doc.delivery_to_type, + pickup_address_name: frm.doc.pickup_address_name, + delivery_address_name: frm.doc.delivery_address_name, + shipment_parcel: frm.doc.shipment_parcel, + description_of_content: frm.doc.description_of_content, + pickup_date: frm.doc.pickup_date, + pickup_contact_name: frm.doc.pickup_contact_name, + delivery_contact_name: frm.doc.delivery_contact_name, + value_of_goods: frm.doc.value_of_goods, + service_data: service_data, + shipment_notific_email: shipment_notific_email, + tracking_notific_email: tracking_notific_email, + delivery_notes: delivery_notes + }, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + frappe.msgprint(__("Shipment created with {0}, ID is {1}", [r.message.service_provider, r.message.shipment_id])); + frm.events.update_tracking(frm, r.message.service_provider, r.message.shipment_id); + } + } + }); + d.hide(); + }; + d.show(); +} diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json new file mode 100644 index 0000000000..b6656a2b72 --- /dev/null +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -0,0 +1,478 @@ +{ + "actions": [], + "autoname": "SHIPMENT-.#####", + "creation": "2020-07-09 10:58:52.508703", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "heading_pickup_from", + "pickup_from_type", + "pickup_company", + "pickup_customer", + "pickup_supplier", + "pickup", + "pickup_address_name", + "pickup_address", + "pickup_contact_name", + "pickup_contact_email", + "pickup_contact", + "column_break_2", + "heading_delivery_to", + "delivery_to_type", + "delivery_company", + "delivery_customer", + "delivery_supplier", + "delivery_to", + "delivery_address_name", + "delivery_address", + "delivery_contact_name", + "delivery_contact_email", + "delivery_contact", + "notification_details_section", + "pickup_from_send_shipping_notification", + "pickup_from_subscribe_to_status_updates", + "shipment_notification_subscriptions", + "column_break_27", + "delivery_to_send_shipping_notification", + "delivery_to_subscribe_to_status_updates", + "shipment_status_update_subscriptions", + "parcels_section", + "shipment_parcel", + "parcel_template", + "add_template", + "column_break_28", + "shipment_delivery_notes", + "shipment_details_section", + "pallets", + "value_of_goods", + "pickup_date", + "pickup_from", + "pickup_to", + "column_break_36", + "shipment_type", + "pickup_type", + "incoterm", + "description_of_content", + "section_break_40", + "shipment_information_section", + "service_provider", + "shipment_id", + "shipment_amount", + "status", + "tracking_url", + "column_break_55", + "carrier", + "carrier_service", + "awb_number", + "tracking_status", + "tracking_status_info", + "amended_from" + ], + "fields": [ + { + "fieldname": "heading_pickup_from", + "fieldtype": "Heading", + "label": "Pickup from" + }, + { + "default": "Company", + "fieldname": "pickup_from_type", + "fieldtype": "Select", + "label": "Pickup from", + "options": "Company\nCustomer\nSupplier" + }, + { + "depends_on": "eval:doc.pickup_from_type == 'Company'", + "fieldname": "pickup_company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "depends_on": "eval:doc.pickup_from_type == 'Customer'", + "fieldname": "pickup_customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, + { + "depends_on": "eval:doc.pickup_from_type == 'Supplier'", + "fieldname": "pickup_supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "fieldname": "pickup", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "label": "Pickup From", + "read_only": 1 + }, + { + "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type == \"Company\"", + "fieldname": "pickup_address_name", + "fieldtype": "Link", + "label": "Address", + "options": "Address", + "reqd": 1 + }, + { + "fieldname": "pickup_address", + "fieldtype": "Small Text", + "read_only": 1 + }, + { + "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type == \"Company\"", + "fieldname": "pickup_contact_name", + "fieldtype": "Link", + "label": "Contact", + "options": "Contact" + }, + { + "fieldname": "pickup_contact_email", + "fieldtype": "Data", + "hidden": 1, + "label": "Contact Email", + "read_only": 1 + }, + { + "fieldname": "pickup_contact", + "fieldtype": "Small Text", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "heading_delivery_to", + "fieldtype": "Heading", + "label": "Delivery to" + }, + { + "default": "Customer", + "fieldname": "delivery_to_type", + "fieldtype": "Select", + "label": "Delivery to", + "options": "Company\nCustomer\nSupplier" + }, + { + "depends_on": "eval:doc.delivery_to_type == 'Company'", + "fieldname": "delivery_company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "depends_on": "eval:doc.delivery_to_type == 'Customer'", + "fieldname": "delivery_customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, + { + "depends_on": "eval:doc.delivery_to_type == 'Supplier'", + "fieldname": "delivery_supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "fieldname": "delivery_to", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "label": "Delivery To", + "read_only": 1 + }, + { + "depends_on": "eval: doc.delivery_customer || doc.delivery_supplier || doc.delivery_to_type == \"Company\"", + "fieldname": "delivery_address_name", + "fieldtype": "Link", + "label": "Address", + "options": "Address", + "reqd": 1 + }, + { + "fieldname": "delivery_address", + "fieldtype": "Small Text", + "read_only": 1 + }, + { + "depends_on": "eval: doc.delivery_customer || doc.delivery_supplier || doc.delivery_to_type == \"Company\"", + "fieldname": "delivery_contact_name", + "fieldtype": "Link", + "label": "Contact", + "options": "Contact" + }, + { + "fieldname": "delivery_contact_email", + "fieldtype": "Data", + "hidden": 1, + "label": "Contact Email", + "read_only": 1 + }, + { + "depends_on": "eval:doc.delivery_contact_name", + "fieldname": "delivery_contact", + "fieldtype": "Small Text", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "notification_details_section", + "fieldtype": "Section Break", + "label": "Notification Details" + }, + { + "default": "0", + "fieldname": "pickup_from_send_shipping_notification", + "fieldtype": "Check", + "label": "Send shipping notification" + }, + { + "default": "0", + "fieldname": "pickup_from_subscribe_to_status_updates", + "fieldtype": "Check", + "label": "Subscribe to status updates" + }, + { + "fieldname": "shipment_notification_subscriptions", + "fieldtype": "Table", + "label": "Shipment Notification Subscriptions", + "options": "Shipment Notification Subscriptions" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "delivery_to_send_shipping_notification", + "fieldtype": "Check", + "label": "Send shipping notification" + }, + { + "default": "0", + "fieldname": "delivery_to_subscribe_to_status_updates", + "fieldtype": "Check", + "label": "Subscribe to status updates" + }, + { + "fieldname": "shipment_status_update_subscriptions", + "fieldtype": "Table", + "label": "Shipment Status Update Subscriptions", + "options": "Shipment Status Update Subscriptions" + }, + { + "fieldname": "parcels_section", + "fieldtype": "Section Break", + "label": "Parcels" + }, + { + "fieldname": "shipment_parcel", + "fieldtype": "Table", + "label": "Shipment Parcel", + "options": "Shipment Parcel" + }, + { + "fieldname": "parcel_template", + "fieldtype": "Link", + "label": "Parcel Template", + "options": "Shipment Parcel Template" + }, + { + "fieldname": "add_template", + "fieldtype": "Button", + "label": "Add Template" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipment_delivery_notes", + "fieldtype": "Table", + "label": "Shipment Delivery Notes", + "options": "Shipment Delivery Notes" + }, + { + "fieldname": "shipment_details_section", + "fieldtype": "Section Break", + "label": "Shipment details" + }, + { + "default": "No", + "fieldname": "pallets", + "fieldtype": "Select", + "label": "Pallets", + "options": "No\nYes" + }, + { + "fieldname": "value_of_goods", + "fieldtype": "Currency", + "label": "Value of Goods", + "precision": "2", + "reqd": 1 + }, + { + "fieldname": "pickup_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Pickup Date", + "reqd": 1 + }, + { + "default": "09:00", + "fieldname": "pickup_from", + "fieldtype": "Select", + "label": "Pickup from", + "options": "09:00\n09:30\n10:00\n10:30\n11:00\n11:30\n12:00\n12:30\n13:00\n13:30\n14:00\n14:30\n15:00\n15:30\n16:00\n16:30\n17:00\n17:30\n18:00\n18:30\n19:00" + }, + { + "default": "17:00", + "fieldname": "pickup_to", + "fieldtype": "Select", + "label": "Pickup to", + "options": "09:00\n09:30\n10:00\n10:30\n11:00\n11:30\n12:00\n12:30\n13:00\n13:30\n14:00\n14:30\n15:00\n15:30\n16:00\n16:30\n17:00\n17:30\n18:00\n18:30\n19:00" + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "default": "Goods", + "fieldname": "shipment_type", + "fieldtype": "Select", + "label": "Shipment Type", + "options": "Goods\nDocuments" + }, + { + "default": "Pickup", + "fieldname": "pickup_type", + "fieldtype": "Select", + "label": "Pickup Type", + "options": "Pickup\nSelf delivery" + }, + { + "fieldname": "description_of_content", + "fieldtype": "Small Text", + "label": "Description of Content", + "reqd": 1 + }, + { + "fieldname": "section_break_40", + "fieldtype": "Section Break" + }, + { + "fieldname": "shipment_information_section", + "fieldtype": "Section Break", + "label": "Shipment Information" + }, + { + "fieldname": "service_provider", + "fieldtype": "Read Only", + "label": "Service Provider" + }, + { + "fieldname": "shipment_id", + "fieldtype": "Read Only", + "label": "Shipment ID" + }, + { + "fieldname": "shipment_amount", + "fieldtype": "Currency", + "label": "Shipment Amount", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted", + "read_only": 1 + }, + { + "fieldname": "tracking_url", + "fieldtype": "Small Text", + "label": "Tracking URL", + "read_only": 1 + }, + { + "fieldname": "carrier", + "fieldtype": "Read Only", + "label": "Carrier" + }, + { + "fieldname": "carrier_service", + "fieldtype": "Read Only", + "label": "Carrier Service" + }, + { + "fieldname": "awb_number", + "fieldtype": "Read Only", + "label": "AWB Number" + }, + { + "fieldname": "tracking_status", + "fieldtype": "Select", + "label": "Tracking Status", + "options": "\nIn Progress\nDelivered\nReturned\nLost", + "read_only": 1 + }, + { + "fieldname": "tracking_status_info", + "fieldtype": "Data", + "label": "Tracking Status Info", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 1, + "label": "Amended From", + "no_copy": 1, + "options": "Shipment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "fieldname": "incoterm", + "fieldtype": "Select", + "label": "Incoterm", + "options": "EXW (Ex Works)\nFCA (Free Carrier)\nCPT (Carriage Paid To)\nCIP (Carriage and Insurance Paid to)\nDPU (Delivered At Place Unloaded)\nDAP (Delivered At Place)\nDDP (Delivered Duty Paid)" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-07-24 11:44:30.904612", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py new file mode 100644 index 0000000000..e059bacfa1 --- /dev/null +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -0,0 +1,300 @@ +# -*- 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 +import json +from frappe import _ +from frappe.model.document import Document +from erpnext.accounts.party import get_party_shipping_address +from frappe.contacts.doctype.contact.contact import get_default_contact +from erpnext.erpnext_integrations.doctype.letmeship.letmeship import LETMESHIP_PROVIDER, get_letmeship_available_services, create_letmeship_shipment, get_letmeship_label, get_letmeship_tracking_data +from erpnext.erpnext_integrations.doctype.packlink.packlink import PACKLINK_PROVIDER, get_packlink_available_services, create_packlink_shipment, get_packlink_label, get_packlink_tracking_data +from erpnext.erpnext_integrations.doctype.sendcloud.sendcloud import SENDCLOUD_PROVIDER, get_sendcloud_available_services, create_sendcloud_shipment, get_sendcloud_label, get_sendcloud_tracking_data +from erpnext.stock.doctype.parcel_service_type.parcel_service_type import match_parcel_service_type_alias + +class Shipment(Document): + def validate(self): + self.validate_weight() + if self.docstatus == 0: + self.status = 'Draft' + + def on_submit(self): + if not self.shipment_parcel: + frappe.throw(_('Please enter Shipment Parcel information')) + if self.value_of_goods == 0: + frappe.throw(_('Value of goods cannot be 0')) + self.status = 'Submitted' + + def on_cancel(self): + self.status = 'Cancelled' + + def validate_weight(self): + for parcel in self.shipment_parcel: + if parcel.weight <= 0: + frappe.throw(_('Parcel weight cannot be 0')) + +@frappe.whitelist() +def fetch_shipping_rates(pickup_from_type, delivery_to_type, pickup_address_name, delivery_address_name, + shipment_parcel, description_of_content, pickup_date, value_of_goods, + pickup_contact_name=None, delivery_contact_name=None): + # Return Shipping Rates for the various Shipping Providers + shipment_prices = [] + letmeship_enabled = frappe.db.get_single_value('LetMeShip','enabled') + packlink_enabled = frappe.db.get_single_value('Packlink','enabled') + sendcloud_enabled = frappe.db.get_single_value('SendCloud','enabled') + pickup_address = get_address(pickup_address_name) + delivery_address = get_address(delivery_address_name) + if letmeship_enabled: + pickup_contact = None + delivery_contact = None + if pickup_from_type != 'Company': + pickup_contact = get_contact(pickup_contact_name) + else: + pickup_contact = get_company_contact() + + if delivery_to_type != 'Company': + delivery_contact = get_contact(delivery_contact_name) + else: + delivery_contact = get_company_contact() + letmeship_prices = get_letmeship_available_services( + delivery_to_type=delivery_to_type, + pickup_address=pickup_address, + delivery_address=delivery_address, + shipment_parcel=shipment_parcel, + description_of_content=description_of_content, + pickup_date=pickup_date, + value_of_goods=value_of_goods, + pickup_contact=pickup_contact, + delivery_contact=delivery_contact, + ) + letmeship_prices = match_parcel_service_type_carrier(letmeship_prices, ['carrier', 'carrier_name']) + shipment_prices = shipment_prices + letmeship_prices + if packlink_enabled: + packlink_prices = get_packlink_available_services( + pickup_address=pickup_address, + delivery_address=delivery_address, + shipment_parcel=shipment_parcel, + pickup_date=pickup_date + ) + packlink_prices = match_parcel_service_type_carrier(packlink_prices, ['carrier_name', 'carrier']) + shipment_prices = shipment_prices + packlink_prices + if sendcloud_enabled and pickup_from_type == 'Company': + sendcloud_prices = get_sendcloud_available_services( + delivery_address=delivery_address, + shipment_parcel=shipment_parcel + ) + shipment_prices = shipment_prices + sendcloud_prices + shipment_prices = sorted(shipment_prices, key=lambda k:k['total_price']) + return shipment_prices + +@frappe.whitelist() +def create_shipment(shipment, pickup_from_type, delivery_to_type, pickup_address_name, + delivery_address_name, shipment_parcel, description_of_content, pickup_date, + value_of_goods, service_data, shipment_notific_email, tracking_notific_email, + pickup_contact_name=None, delivery_contact_name=None, delivery_notes=[]): + # Create Shipment for the selected provider + service_info = json.loads(service_data) + shipment_info = None + pickup_contact = None + delivery_contact = None + pickup_address = get_address(pickup_address_name) + delivery_address = get_address(delivery_address_name) + if pickup_from_type != 'Company': + pickup_contact = get_contact(pickup_contact_name) + else: + pickup_contact = get_company_contact() + + if delivery_to_type != 'Company': + delivery_contact = get_contact(delivery_contact_name) + else: + delivery_contact = get_company_contact() + if service_info['service_provider'] == LETMESHIP_PROVIDER: + shipment_info = create_letmeship_shipment( + pickup_address=pickup_address, + delivery_address=delivery_address, + shipment_parcel=shipment_parcel, + description_of_content=description_of_content, + pickup_date=pickup_date, + value_of_goods=value_of_goods, + pickup_contact=pickup_contact, + delivery_contact=delivery_contact, + service_info=service_info, + shipment_notific_email=shipment_notific_email, + tracking_notific_email=tracking_notific_email, + ) + + if service_info['service_provider'] == PACKLINK_PROVIDER: + shipment_info = create_packlink_shipment( + pickup_address=pickup_address, + delivery_address=delivery_address, + shipment_parcel=shipment_parcel, + description_of_content=description_of_content, + pickup_date=pickup_date, + value_of_goods=value_of_goods, + pickup_contact=pickup_contact, + delivery_contact=delivery_contact, + service_info=service_info, + ) + + if service_info['service_provider'] == SENDCLOUD_PROVIDER: + shipment_info = create_sendcloud_shipment( + shipment=shipment, + delivery_address=delivery_address, + shipment_parcel=shipment_parcel, + description_of_content=description_of_content, + value_of_goods=value_of_goods, + delivery_contact=delivery_contact, + service_info=service_info, + ) + + if shipment_info: + frappe.db.set_value('Shipment', shipment, 'service_provider', shipment_info.get('service_provider')) + frappe.db.set_value('Shipment', shipment, 'carrier', shipment_info.get('carrier')) + frappe.db.set_value('Shipment', shipment, 'carrier_service', shipment_info.get('carrier_service')) + frappe.db.set_value('Shipment', shipment, 'shipment_id', shipment_info.get('shipment_id')) + frappe.db.set_value('Shipment', shipment, 'shipment_amount', shipment_info.get('shipment_amount')) + frappe.db.set_value('Shipment', shipment, 'awb_number', shipment_info.get('awb_number')) + frappe.db.set_value('Shipment', shipment, 'status', 'Booked') + if delivery_notes: + update_delivery_note(delivery_notes=delivery_notes, shipment_info=shipment_info) + return shipment_info + + +@frappe.whitelist() +def print_shipping_label(service_provider, shipment_id): + if service_provider == LETMESHIP_PROVIDER: + shipping_label = get_letmeship_label(shipment_id) + elif service_provider == PACKLINK_PROVIDER: + shipping_label = get_packlink_label(shipment_id) + elif service_provider == SENDCLOUD_PROVIDER: + shipping_label = get_sendcloud_label(shipment_id) + return shipping_label + + +@frappe.whitelist() +def update_tracking(shipment, service_provider, shipment_id, delivery_notes=[]): + # Update Tracking info in Shipment + tracking_data = None + if service_provider == LETMESHIP_PROVIDER: + tracking_data = get_letmeship_tracking_data(shipment_id) + elif service_provider == PACKLINK_PROVIDER: + tracking_data = get_packlink_tracking_data(shipment_id) + elif service_provider == SENDCLOUD_PROVIDER: + tracking_data = get_sendcloud_tracking_data(shipment_id) + if tracking_data: + if delivery_notes: + update_delivery_note(delivery_notes=delivery_notes, tracking_info=tracking_data) + frappe.db.set_value('Shipment', shipment, 'awb_number', tracking_data.get('awb_number')) + frappe.db.set_value('Shipment', shipment, 'tracking_status', tracking_data.get('tracking_status')) + frappe.db.set_value('Shipment', shipment, 'tracking_status_info', tracking_data.get('tracking_status_info')) + frappe.db.set_value('Shipment', shipment, 'tracking_url', tracking_data.get('tracking_url')) + +@frappe.whitelist() +def get_address_name(ref_doctype, docname): + # Return address name + return get_party_shipping_address(ref_doctype, docname) + +@frappe.whitelist() +def get_contact_name(ref_doctype, docname): + # Return address name + return get_default_contact(ref_doctype, docname) + +def update_delivery_note(delivery_notes, shipment_info=None, tracking_info=None): + # Update Shipment Info in Delivery Note + # Using db_set since some services might not exist + for delivery_note in json.loads(delivery_notes): + dl_doc = frappe.get_doc('Delivery Note', delivery_note) + if shipment_info: + dl_doc.db_set('delivery_type', 'Parcel Service') + dl_doc.db_set('parcel_service', shipment_info.get('carrier')) + dl_doc.db_set('parcel_service_type', shipment_info.get('carrier_service')) + if tracking_info: + dl_doc.db_set('tracking_number', tracking_info.get('awb_number')) + dl_doc.db_set('tracking_url', tracking_info.get('tracking_url')) + dl_doc.db_set('tracking_status', tracking_info.get('tracking_status')) + dl_doc.db_set('tracking_status_info', tracking_info.get('tracking_status_info')) + + +def update_tracking_info(): + # Daily scheduled event to update Tracking info for not delivered Shipments + # Also Updates the related Delivery Notes + shipments = frappe.get_all('Shipment', filters={ + 'docstatus': 1, + 'status': 'Booked', + 'shipment_id': ['!=', ''], + 'tracking_status': ['!=', 'Delivered'], + }) + for shipment in shipments: + shipment_doc = frappe.get_doc('Shipment', shipment.name) + tracking_info = \ + update_tracking( + shipment_doc.service_provider, + shipment_doc.shipment_id, + shipment_doc.shipment_delivery_notes + ) + if tracking_info: + shipment_doc.db_set('awb_number', tracking_info.get('awb_number')) + shipment_doc.db_set('tracking_url', tracking_info.get('tracking_url')) + shipment_doc.db_set('tracking_status', tracking_info.get('tracking_status')) + shipment_doc.db_set('tracking_status_info', tracking_info.get('tracking_status_info')) + + +def get_address(address_name): + address = frappe.db.get_value('Address', address_name, [ + 'address_title', + 'address_line1', + 'address_line2', + 'city', + 'pincode', + 'country', + ], as_dict=1) + address.country_code = frappe.db.get_value('Country', address.country, 'code').upper() + if not address.pincode or address.pincode == '': + frappe.throw(_("Postal Code is mandatory to continue.
\ + Please set Postal Code for Address {1}" + ).format(address_name, address_name)) + address.pincode = address.pincode.replace(' ', '') + address.city = address.city.strip() + return address + + +def get_contact(contact_name): + contact = frappe.db.get_value('Contact', contact_name, [ + 'first_name', + 'last_name', + 'email_id', + 'phone', + 'mobile_no', + 'gender', + ], as_dict=1) + if not contact.last_name: + frappe.throw(_("Last Name is mandatory to continue.
\ + Please set Last Name for Contact {1}" + ).format(contact_name, contact_name)) + if not contact.phone: + contact.phone = contact.mobile_no + return contact + + +def get_company_contact(): + contact = frappe.db.get_value('User', frappe.session.user, [ + 'first_name', + 'last_name', + 'email', + 'phone', + 'mobile_no', + 'gender', + ], as_dict=1) + if not contact.phone: + contact.phone = contact.mobile_no + return contact + +def match_parcel_service_type_carrier(shipment_prices, reference): + for idx, prices in enumerate(shipment_prices): + service_name = match_parcel_service_type_alias(prices.get(reference[0]), prices.get(reference[1])) + is_preferred = frappe.db.get_value('Parcel Service Type', service_name, 'show_in_preferred_services_list') + shipment_prices[idx].service_name = service_name + shipment_prices[idx].is_preferred = is_preferred + return shipment_prices diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js new file mode 100644 index 0000000000..57e92099cb --- /dev/null +++ b/erpnext/stock/doctype/shipment/shipment_list.js @@ -0,0 +1,8 @@ +frappe.listview_settings['Shipment'] = { + add_fields: ["status"], + get_indicator: function(doc) { + if(doc.status=='Booked') { + return [__("Booked"), "green"]; + } + } +}; \ No newline at end of file diff --git a/erpnext/stock/doctype/shipment/shipment_service_selector.html b/erpnext/stock/doctype/shipment/shipment_service_selector.html new file mode 100644 index 0000000000..ed9b8bf400 --- /dev/null +++ b/erpnext/stock/doctype/shipment/shipment_service_selector.html @@ -0,0 +1,70 @@ +{% if (data.length) { %} +
+
{{ __("Preferred Services") }}
+ + + + {% for (var i = 0; i < header_columns.length; i++) { %} + + {% } %} + + + + {% for (var i = 0; i < data.length; i++) { %} + {% if (data[i].is_preferred) { %} + + + + + + + + {% } %} + {% } %} + +
{{ header_columns[i] }}
{{ data[i].service_provider }}{{ data[i].carrier }}{{ data[i].service_name }}{{ format_currency(data[i].total_price, 'EUR', 2) }} + +
+
{{ __("Other Services") }}
+ + + + {% for (var i = 0; i < header_columns.length; i++) { %} + + {% } %} + + + + {% for (var i = 0; i < data.length; i++) { %} + {% if (!data[i].is_preferred) { %} + + + + + + + + {% } %} + {% } %} + +
{{ header_columns[i] }}
{{ data[i].service_provider }}{{ data[i].carrier }}{{ data[i].service_name }}{{ format_currency(data[i].total_price, 'EUR', 2) }} + +
+
+{% } else { %} +
{{ __("No Services Available") }}
+{% } %} + + \ No newline at end of file diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py new file mode 100644 index 0000000000..6a06930e82 --- /dev/null +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals +import json +from datetime import date, timedelta + +import frappe +import unittest +from erpnext.stock.doctype.shipment.shipment import fetch_shipping_rates +from erpnext.stock.doctype.shipment.shipment import create_shipment +from erpnext.stock.doctype.shipment.shipment import update_tracking + +class TestShipment(unittest.TestCase): + pass + + def test_shipment_booking(self): + shipment = create_test_shipment() + try: + shipment.submit() + except: + frappe.throw('Error occurred on submit shipment') + doc, rate, tracking_data = make_shipment_transaction(shipment) + if doc and rate and tracking_data: + self.assertEqual(doc.service_provider, rate.get('service_provider')) + self.assertEqual(doc.shipment_amount, rate.get('actual_price')) + self.assertEqual(doc.carrier, rate.get('carrier')) + self.assertEqual(doc.tracking_status, tracking_data.get('tracking_status')) + self.assertEqual(doc.tracking_url, tracking_data.get('tracking_url')) + + def test_shipment_from_delivery_note(self): + delivery_note = create_test_delivery_note() + try: + delivery_note.submit() + except: + frappe.throw('An error occurred.') + + shipment = create_test_shipment([ delivery_note ]) + try: + shipment.submit() + except: + frappe.throw('Error occurred on submit shipment') + doc, rate, tracking_data = make_shipment_transaction(shipment) + if doc and rate and tracking_data: + self.assertEqual(doc.service_provider, rate.get('service_provider')) + self.assertEqual(doc.shipment_amount, rate.get('actual_price')) + self.assertEqual(doc.carrier, rate.get('carrier')) + self.assertEqual(doc.tracking_status, tracking_data.get('tracking_status')) + self.assertEqual(doc.tracking_url, tracking_data.get('tracking_url')) + + + +def make_shipment_transaction(shipment): + shipment_parcel = convert_shipmet_parcel(shipment.shipment_parcel) + shipment_rates = fetch_shipping_rates(shipment.pickup_from_type, shipment.delivery_to_type, + shipment.pickup_address_name, shipment.delivery_address_name, + shipment_parcel, shipment.description_of_content, + shipment.pickup_date, shipment.value_of_goods, + pickup_contact_name=shipment.pickup_contact_name, + delivery_contact_name=shipment.delivery_contact_name + ) + if len(shipment_rates) > 0: + # We are taking the first shipment rate + rate = shipment_rates[0] + new_shipment = create_shipment( + shipment=shipment.name, + pickup_from_type=shipment.pickup_from_type, + delivery_to_type=shipment.delivery_to_type, + pickup_address_name=shipment.pickup_address_name, + delivery_address_name=shipment.delivery_address_name, + shipment_parcel=shipment_parcel, + description_of_content=shipment.description_of_content, + pickup_date=shipment.pickup_date, + pickup_contact_name=shipment.pickup_contact_name, + delivery_contact_name=shipment.delivery_contact_name, + value_of_goods=shipment.value_of_goods, + service_data=json.dumps(rate), + shipment_notific_email=None, + tracking_notific_email=None, + delivery_notes=None + ) + service_provider = rate.get('service_provider') + shipment_id = new_shipment.get('shipment_id') + tracking_data = update_tracking( + shipment.name, + service_provider, + shipment_id, + delivery_notes=None + ) + doc = frappe.get_doc('Shipment', shipment.name) + return doc, rate, tracking_data + return None, None, None + +def create_test_delivery_note(): + company = get_shipment_company() + customer = get_shipment_customer() + item = get_shipment_item(company.name) + posting_date = date.today() + timedelta(days=1) + + create_material_receipt(item, company.name) + delivery_note = frappe.new_doc("Delivery Note") + delivery_note.company = company.name + delivery_note.posting_date = posting_date.strftime("%Y-%m-%d") + delivery_note.posting_time = '10:00' + delivery_note.customer = customer.name + delivery_note.append('items', + { + "item_code": item.name, + "item_name": item.item_name, + "description": 'Test delivery note for shipment', + "qty": 5, + "uom": 'Nos', + "warehouse": 'Stores - SC', + "rate": item.standard_rate, + "cost_center": 'Main - SC' + } + ) + delivery_note.insert() + frappe.db.commit() + return delivery_note + + +def create_test_shipment(delivery_notes=[]): + company = get_shipment_company() + company_address = get_shipment_company_address(company.name) + customer = get_shipment_customer() + customer_address = get_shipment_customer_address(customer.name) + customer_contact = get_shipment_customer_contact(customer.name) + posting_date = date.today() + timedelta(days=5) + + shipment = frappe.new_doc("Shipment") + shipment.pickup_from_type = 'Company' + shipment.pickup_company = company.name + shipment.pickup_address_name = company_address.name + shipment.delivery_to_type = 'Customer' + shipment.delivery_customer = customer.name + shipment.delivery_address_name = customer_address.name + shipment.delivery_contact_name = customer_contact.name + shipment.pallets = 'No' + shipment.shipment_type = 'Goods' + shipment.value_of_goods = 1000 + shipment.pickup_type = 'Pickup' + shipment.pickup_date = posting_date.strftime("%Y-%m-%d") + shipment.pickup_from = '09:00' + shipment.pickup_to = '17:00' + shipment.description_of_content = 'unit test entry' + for delivery_note in delivery_notes: + shipment.append('shipment_delivery_notes', + { + "delivery_note": delivery_note.name + } + ) + shipment.append('shipment_parcel', + { + "length": 5, + "width": 5, + "height": 5, + "weight": 5, + "count": 5 + } + ) + shipment.insert() + frappe.db.commit() + return shipment + + +def get_shipment_customer_contact(customer_name): + contact_fname = 'Customer Shipment' + contact_lname = 'Testing' + customer_name = contact_fname + ' ' + contact_lname + contacts = frappe.get_all("Contact", fields=["name"], filters = {"name": customer_name}) + if len(contacts): + return contacts[0] + else: + return create_customer_contact(contact_fname, contact_lname) + + +def get_shipment_customer_address(customer_name): + address_title = customer_name + ' address 123' + customer_address = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title}) + if len(customer_address): + return customer_address[0] + else: + return create_shipment_address(address_title, customer_name, 81929) + +def get_shipment_customer(): + customer_name = 'Shipment Customer' + customer = frappe.get_all("Customer", fields=["name"], filters = {"name": customer_name}) + if len(customer): + return customer[0] + else: + return create_shipment_customer(customer_name) + +def get_shipment_company_address(company_name): + address_title = company_name + ' address 123' + addresses = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title}) + if len(addresses): + return addresses[0] + else: + return create_shipment_address(address_title, company_name, 80331) + +def get_shipment_company(): + company_name = 'Shipment Company' + abbr = 'SC' + companies = frappe.get_all("Company", fields=["name"], filters = {"company_name": company_name}) + if len(companies): + return companies[0] + else: + return create_shipment_company(company_name, abbr) + +def get_shipment_item(company_name): + item_name = 'Testing Shipment item' + items = frappe.get_all("Item", + fields=["name", "item_name", "item_code", "standard_rate"], + filters = {"item_name": item_name} + ) + if len(items): + return items[0] + else: + return create_shipment_item(item_name, company_name) + +def create_shipment_address(address_title, company_name, postal_code): + address = frappe.new_doc("Address") + address.address_title = address_title + address.address_type = 'Shipping' + address.address_line1 = company_name + ' address line 1' + address.city = 'Random City' + address.postal_code = postal_code + address.country = 'Germany' + address.insert() + return address + + +def create_customer_contact(fname, lname): + customer = frappe.new_doc("Contact") + customer.customer_name = fname + ' ' + lname + customer.first_name = fname + customer.last_name = lname + customer.is_primary_contact = 1 + customer.is_billing_contact = 1 + customer.append('email_ids', + { + 'email_id': 'randomme@email.com', + 'is_primary': 1 + } + ) + customer.append('phone_nos', + { + 'phone': '123123123', + 'is_primary_phone': 1, + 'is_primary_mobile_no': 1 + } + ) + customer.status = 'Passive' + customer.insert() + return customer + + +def create_shipment_company(company_name, abbr): + company = frappe.new_doc("Company") + company.company_name = company_name + company.abbr = abbr + company.default_currency = 'EUR' + company.country = 'Germany' + company.insert() + return company + +def create_shipment_customer(customer_name): + customer = frappe.new_doc("Customer") + customer.customer_name = customer_name + customer.customer_type = 'Company' + customer.customer_group = 'All Customer Groups' + customer.territory = 'All Territories' + customer.gst_category = 'Unregistered' + customer.insert() + return customer + +def create_material_receipt(item, company): + posting_date = date.today() + stock = frappe.new_doc("Stock Entry") + stock.company = company + stock.stock_entry_type = 'Material Receipt' + stock.posting_date = posting_date.strftime("%Y-%m-%d") + stock.append('items', + { + "t_warehouse": 'Stores - SC', + "item_code": item.name, + "qty": 5, + "uom": 'Nos', + "basic_rate": item.standard_rate, + "cost_center": 'Main - SC' + } + ) + stock.insert() + try: + stock.submit() + except: + frappe.throw('An error occurred.') + + +def create_shipment_item(item_name, company_name): + item = frappe.new_doc("Item") + item.item_name = item_name + item.item_code = item_name + item.item_group = 'All Item Groups' + item.opening_stock = 'Nos' + item.standard_rate = 50 + item.append('item_defaults', + { + "company": company_name, + "default_warehouse": 'Stores - SC' + } + ) + try: + item.insert() + except: + frappe.throw('An error occurred.') + return item + + +def convert_shipmet_parcel(shipmet_parcel): + data = [] + for parcel in shipmet_parcel: + data.append( + { + "length": parcel.length, + "width": parcel.width, + "height": parcel.height, + "weight": parcel.weight, + "count": parcel.count + } + ) + return json.dumps(data) diff --git a/erpnext/stock/doctype/shipment_delivery_notes/__init__.py b/erpnext/stock/doctype/shipment_delivery_notes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json b/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json new file mode 100644 index 0000000000..fbc01d9a24 --- /dev/null +++ b/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "creation": "2020-07-09 11:52:57.939021", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "delivery_note", + "grand_total" + ], + "fields": [ + { + "fieldname": "delivery_note", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Delivery Note", + "options": "Delivery Note", + "reqd": 1 + }, + { + "fetch_from": "delivery_note.grand_total", + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Value", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-09 12:55:01.134270", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment Delivery Notes", + "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/shipment_delivery_notes/shipment_delivery_notes.py b/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.py new file mode 100644 index 0000000000..ed936c60f8 --- /dev/null +++ b/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.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 ShipmentDeliveryNotes(Document): + pass diff --git a/erpnext/stock/doctype/shipment_notification_subscriptions/__init__.py b/erpnext/stock/doctype/shipment_notification_subscriptions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json b/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json new file mode 100644 index 0000000000..bd9b8003a8 --- /dev/null +++ b/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2020-07-09 12:49:09.185552", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email", + "unsubscribed" + ], + "fields": [ + { + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "email", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "unsubscribed", + "fieldtype": "Check", + "in_list_view": 1, + "label": "unsubscribed" + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-09 12:55:14.217387", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment Notification Subscriptions", + "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/shipment_notification_subscriptions/shipment_notification_subscriptions.py b/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.py new file mode 100644 index 0000000000..28ead7fab8 --- /dev/null +++ b/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.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 ShipmentNotificationSubscriptions(Document): + pass diff --git a/erpnext/stock/doctype/shipment_parcel/__init__.py b/erpnext/stock/doctype/shipment_parcel/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json new file mode 100644 index 0000000000..6943edcdc9 --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json @@ -0,0 +1,65 @@ +{ + "actions": [], + "creation": "2020-07-09 11:28:48.887737", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "length", + "width", + "height", + "weight", + "count" + ], + "fields": [ + { + "fieldname": "length", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Length (cm)", + "reqd": 1 + }, + { + "fieldname": "width", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Width (cm)", + "reqd": 1 + }, + { + "fieldname": "height", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Height (cm)", + "reqd": 1 + }, + { + "fieldname": "weight", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Weight (kg)", + "precision": "1", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "count", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Count", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-09 12:54:14.847170", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment Parcel", + "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/shipment_parcel/shipment_parcel.py b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py new file mode 100644 index 0000000000..53e6ed55dd --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.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 ShipmentParcel(Document): + pass diff --git a/erpnext/stock/doctype/shipment_parcel_template/__init__.py b/erpnext/stock/doctype/shipment_parcel_template/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js new file mode 100644 index 0000000000..785a3b304d --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.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('Shipment Parcel Template', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json new file mode 100644 index 0000000000..ec2bb1c9b3 --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json @@ -0,0 +1,78 @@ +{ + "actions": [], + "autoname": "field:preset_name", + "creation": "2020-07-09 11:43:43.470339", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "preset_name", + "length", + "width", + "height", + "weight" + ], + "fields": [ + { + "fieldname": "preset_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Preset Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "length", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Length (cm)", + "reqd": 1 + }, + { + "fieldname": "width", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Width (cm)", + "reqd": 1 + }, + { + "fieldname": "height", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Height (cm)", + "reqd": 1 + }, + { + "fieldname": "weight", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Weight (kg)", + "precision": "1", + "reqd": 1 + } + ], + "links": [], + "modified": "2020-07-10 12:53:22.772826", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment Parcel Template", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py new file mode 100644 index 0000000000..2a8d58d830 --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.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 ShipmentParcelTemplate(Document): + pass diff --git a/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py new file mode 100644 index 0000000000..6e2caa768b --- /dev/null +++ b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.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 TestShipmentParcelTemplate(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/shipment_status_update_subscriptions/__init__.py b/erpnext/stock/doctype/shipment_status_update_subscriptions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json b/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json new file mode 100644 index 0000000000..3b86b400d8 --- /dev/null +++ b/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2020-07-09 12:51:10.656612", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email", + "unsubscribed" + ], + "fields": [ + { + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "email", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "unsubscribed", + "fieldtype": "Check", + "in_list_view": 1, + "label": "unsubscribed" + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-09 12:55:27.615463", + "modified_by": "Administrator", + "module": "Stock", + "name": "Shipment Status Update Subscriptions", + "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/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py b/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py new file mode 100644 index 0000000000..a8e31ea778 --- /dev/null +++ b/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.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 ShipmentStatusUpdateSubscriptions(Document): + pass From 000494f54856fd8abedd71c44c173bd4698bd8f8 Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Thu, 1 Oct 2020 01:23:29 +0000 Subject: [PATCH 203/283] fix(shipment): apply code review --- .../doctype/letmeship/letmeship.json | 4 +- .../doctype/letmeship/letmeship.py | 262 +++++++++--------- .../doctype/packlink/packlink.py | 3 + .../doctype/sendcloud/sendcloud.json | 4 +- .../doctype/sendcloud/sendcloud.py | 19 +- erpnext/erpnext_integrations/utils.py | 2 - .../doctype/delivery_note/delivery_note.py | 28 +- erpnext/stock/doctype/shipment/api/utils.py | 67 ----- erpnext/stock/doctype/shipment/shipment.js | 203 ++++++++------ erpnext/stock/doctype/shipment/shipment.json | 93 ++++--- erpnext/stock/doctype/shipment/shipment.py | 42 ++- .../shipment/shipment_service_selector.html | 88 +++--- .../__init__.py | 0 .../shipment_delivery_note.json} | 2 +- .../shipment_delivery_note.py} | 2 +- .../__init__.py | 0 .../shipment_notification_subscription.json} | 2 +- .../shipment_notification_subscription.py} | 2 +- .../shipment_parcel_template.json | 22 +- .../__init__.py | 0 .../shipment_status_update_subscription.json} | 2 +- .../shipment_status_update_subscription.py} | 2 +- 22 files changed, 405 insertions(+), 444 deletions(-) delete mode 100644 erpnext/stock/doctype/shipment/api/utils.py rename erpnext/stock/doctype/{shipment_delivery_notes => shipment_delivery_note}/__init__.py (100%) rename erpnext/stock/doctype/{shipment_delivery_notes/shipment_delivery_notes.json => shipment_delivery_note/shipment_delivery_note.json} (95%) rename erpnext/stock/doctype/{shipment_delivery_notes/shipment_delivery_notes.py => shipment_delivery_note/shipment_delivery_note.py} (86%) rename erpnext/stock/doctype/{shipment_notification_subscriptions => shipment_notification_subscription}/__init__.py (100%) rename erpnext/stock/doctype/{shipment_notification_subscriptions/shipment_notification_subscriptions.json => shipment_notification_subscription/shipment_notification_subscription.json} (93%) rename erpnext/stock/doctype/{shipment_notification_subscriptions/shipment_notification_subscriptions.py => shipment_notification_subscription/shipment_notification_subscription.py} (83%) rename erpnext/stock/doctype/{shipment_status_update_subscriptions => shipment_status_update_subscription}/__init__.py (100%) rename erpnext/stock/doctype/{shipment_status_update_subscriptions/shipment_status_update_subscriptions.json => shipment_status_update_subscription/shipment_status_update_subscription.json} (93%) rename erpnext/stock/doctype/{shipment_status_update_subscriptions/shipment_status_update_subscriptions.py => shipment_status_update_subscription/shipment_status_update_subscription.py} (83%) diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json index 4a9a70f251..94b001ed08 100644 --- a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json +++ b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json @@ -24,14 +24,14 @@ }, { "fieldname": "api_password", - "fieldtype": "Data", + "fieldtype": "Password", "label": "API Password", "read_only_depends_on": "eval:doc.enabled == 0" } ], "issingle": 1, "links": [], - "modified": "2020-08-05 16:33:44.548230", + "modified": "2020-10-21 10:28:37.607717", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "LetMeShip", diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py index 3ad06dbb58..162c1edb37 100644 --- a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py +++ b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py @@ -20,9 +20,7 @@ def get_letmeship_available_services(delivery_to_type, pickup_address, delivery_address, shipment_parcel, description_of_content, pickup_date, value_of_goods, pickup_contact=None, delivery_contact=None): # Retrieve rates at LetMeShip from specification stated. - enabled = frappe.db.get_single_value('LetMeShip','enabled') - api_id = frappe.db.get_single_value('LetMeShip','api_id') - api_password = frappe.db.get_single_value('LetMeShip','api_password') + api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id']) if not enabled or not api_id or not api_password: return [] @@ -41,62 +39,16 @@ def get_letmeship_available_services(delivery_to_type, pickup_address, 'Accept': 'application/json', 'Access-Control-Allow-Origin': 'string' } - payload = {'pickupInfo': { - 'address': { - 'countryCode': pickup_address.country_code, - 'zip': pickup_address.pincode, - 'city': pickup_address.city, - 'street': pickup_address.address_line1, - 'addressInfo1': pickup_address.address_line2, - 'houseNo': '', - }, - 'company': pickup_address.address_title, - 'person': { - 'title': pickup_contact.title, - 'firstname': pickup_contact.first_name, - 'lastname': pickup_contact.last_name - }, - 'phone': { - 'phoneNumber': pickup_contact.phone, - 'phoneNumberPrefix': pickup_contact.phone_prefix - }, - 'email': pickup_contact.email, - }, 'deliveryInfo': { - 'address': { - 'countryCode': delivery_address.country_code, - 'zip': delivery_address.pincode, - 'city': delivery_address.city, - 'street': delivery_address.address_line1, - 'addressInfo1': delivery_address.address_line2, - 'houseNo': '', - }, - 'company': delivery_address.address_title, - 'person': { - 'title': delivery_contact.title, - 'firstname': delivery_contact.first_name, - 'lastname': delivery_contact.last_name - }, - 'phone': { - 'phoneNumber': delivery_contact.phone, - 'phoneNumberPrefix': delivery_contact.phone_prefix - }, - 'email': delivery_contact.email, - }, 'shipmentDetails': { - 'contentDescription': description_of_content, - 'shipmentType': 'PARCEL', - 'shipmentSettings': { - 'saturdayDelivery': False, - 'ddp': False, - 'insurance': False, - 'pickupOrder': False, - 'pickupTailLift': False, - 'deliveryTailLift': False, - 'holidayDelivery': False, - }, - 'goodsValue': value_of_goods, - 'parcelList': parcel_list, - 'pickupInterval': {'date': pickup_date}, - }} + payload = generate_payload( + pickup_address=pickup_address, + pickup_contact=pickup_contact, + delivery_address=delivery_address, + delivery_contact=delivery_contact, + description_of_content=description_of_content, + value_of_goods=value_of_goods, + parcel_list=parcel_list, + pickup_date=pickup_date + ) try: available_services = [] response_data = requests.post( @@ -128,6 +80,7 @@ def get_letmeship_available_services(delivery_to_type, pickup_address, .format(response_data['message']) ) except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint( _('Error occurred while fetching LetMeShip Prices: {0}') .format(str(exc)), @@ -142,9 +95,7 @@ def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, pickup_contact=None, delivery_contact=None): # Create a transaction at LetMeShip # LetMeShip have limit of 30 characters for Company field - enabled = frappe.db.get_single_value('LetMeShip','enabled') - api_id = frappe.db.get_single_value('LetMeShip','api_id') - api_password = frappe.db.get_single_value('LetMeShip','api_password') + api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id']) if not enabled or not api_id or not api_password: return [] @@ -162,6 +113,72 @@ def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, 'Accept': 'application/json', 'Access-Control-Allow-Origin': 'string' } + payload = generate_payload( + pickup_address=pickup_address, + pickup_contact=pickup_contact, + delivery_address=delivery_address, + delivery_contact=delivery_contact, + description_of_content=description_of_content, + value_of_goods=value_of_goods, + parcel_list=parcel_list, + pickup_date=pickup_date, + service_info=service_info, + tracking_notific_email=tracking_notific_email, + shipment_notific_email=shipment_notific_email + ) + try: + response_data = requests.post( + url=url, + auth=(api_id, api_password), + headers=headers, + data=json.dumps(payload) + ) + response_data = json.loads(response_data.text) + if 'shipmentId' in response_data: + shipment_amount = response_data['service']['priceInfo']['totalPrice'] + awb_number = '' + url = 'https://api.letmeship.com/v1/shipments/{id}'.format(id=response_data['shipmentId']) + tracking_response = requests.get(url, auth=(api_id, api_password),headers=headers) + tracking_response_data = json.loads(tracking_response.text) + if 'trackingData' in tracking_response_data: + for parcel in tracking_response_data['trackingData']['parcelList']: + if 'awbNumber' in parcel: + awb_number = parcel['awbNumber'] + return { + 'service_provider': LETMESHIP_PROVIDER, + 'shipment_id': response_data['shipmentId'], + 'carrier': service_info['carrier'], + 'carrier_service': service_info['service_name'], + 'shipment_amount': shipment_amount, + 'awb_number': awb_number, + } + elif 'message' in response_data: + frappe.throw( + _('Error occurred while creating Shipment: {0}') + .format(response_data['message']) + ) + except Exception as exc: + frappe.log_error(frappe.get_traceback()) + frappe.msgprint( + _('Error occurred while creating Shipment: {0}') + .format(str(exc)), + indicator='orange', + alert=True + ) + +def generate_payload( + pickup_address, + pickup_contact, + delivery_address, + delivery_contact, + description_of_content, + value_of_goods, + parcel_list, + pickup_date, + service_info=None, + tracking_notific_email=None, + shipment_notific_email=None +): payload = { 'pickupInfo': { 'address': { @@ -205,18 +222,6 @@ def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, }, 'email': delivery_contact.email, }, - 'service': { - 'baseServiceDetails': { - 'id': service_info['id'], - 'name': service_info['service_name'], - 'carrier': service_info['carrier'], - 'priceInfo': service_info['price_info'], - }, - 'supportedExWorkType': [], - 'messages': [''], - 'description': '', - 'serviceInfo': '', - }, 'shipmentDetails': { 'contentDescription': description_of_content, 'shipmentType': 'PARCEL', @@ -233,10 +238,24 @@ def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, 'parcelList': parcel_list, 'pickupInterval': { 'date': pickup_date + } + } + } + + if service_info: + payload['service'] = { + 'baseServiceDetails': { + 'id': service_info['id'], + 'name': service_info['service_name'], + 'carrier': service_info['carrier'], + 'priceInfo': service_info['price_info'], }, - 'contentDescription': description_of_content, - }, - 'shipmentNotification': { + 'supportedExWorkType': [], + 'messages': [''], + 'description': '', + 'serviceInfo': '', + } + payload['shipmentNotification'] = { 'trackingNotification': { 'deliveryNotification': True, 'problemNotification': True, @@ -247,77 +266,47 @@ def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, 'notificationText': '', 'emails': [ shipment_notific_email ] } - }, - 'labelEmail': True, - } + } + payload['labelEmail'] = True + return payload + +def get_letmeship_label(shipment_id): try: - response_data = requests.post( - url=url, - auth=(api_id, api_password), - headers=headers, - data=json.dumps(payload) + # Retrieve shipment label from LetMeShip + api_id = frappe.db.get_single_value('LetMeShip','api_id') + api_password = frappe.db.get_single_value('LetMeShip','api_password') + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Origin': 'string' + } + url = 'https://api.letmeship.com/v1/shipments/{id}/documents?types=LABEL'\ + .format(id=shipment_id) + shipment_label_response = requests.get( + url, + auth=(api_id,api_password), + headers=headers ) - response_data = json.loads(response_data.text) - if 'shipmentId' in response_data: - shipment_amount = response_data['service']['priceInfo']['totalPrice'] - awb_number = '' - url = 'https://api.letmeship.com/v1/shipments/{id}'.format(id=response_data['shipmentId']) - tracking_response = requests.get(url, auth=(api_id, api_password),headers=headers) - tracking_response_data = json.loads(tracking_response.text) - if 'trackingData' in tracking_response_data: - for parcel in tracking_response_data['trackingData']['parcelList']: - if 'awbNumber' in parcel: - awb_number = parcel['awbNumber'] - return { - 'service_provider': LETMESHIP_PROVIDER, - 'shipment_id': response_data['shipmentId'], - 'carrier': service_info['carrier'], - 'carrier_service': service_info['service_name'], - 'shipment_amount': shipment_amount, - 'awb_number': awb_number, - } - elif 'message' in response_data: + shipment_label_response_data = json.loads(shipment_label_response.text) + if 'documents' in shipment_label_response_data: + for label in shipment_label_response_data['documents']: + if 'data' in label: + return json.dumps(label['data']) + else: frappe.throw( - _('Error occurred while creating Shipment: {0}') - .format(response_data['message']) + _('Error occurred while printing Shipment: {0}') + .format(shipment_label_response_data['message']) ) except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint( - _('Error occurred while creating Shipment: {0}') + _('Error occurred while printing Shipment: {0}') .format(str(exc)), indicator='orange', alert=True ) -def get_letmeship_label(shipment_id): - # Retrieve shipment label from LetMeShip - api_id = frappe.db.get_single_value('LetMeShip','api_id') - api_password = frappe.db.get_single_value('LetMeShip','api_password') - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Origin': 'string' - } - url = 'https://api.letmeship.com/v1/shipments/{id}/documents?types=LABEL'\ - .format(id=shipment_id) - shipment_label_response = requests.get( - url, - auth=(api_id,api_password), - headers=headers - ) - shipment_label_response_data = json.loads(shipment_label_response.text) - if 'documents' in shipment_label_response_data: - for label in shipment_label_response_data['documents']: - if 'data' in label: - return json.dumps(label['data']) - else: - frappe.throw( - _('Error occurred while printing Shipment: {0}') - .format(shipment_label_response_data['message']) - ) - - def get_letmeship_tracking_data(shipment_id): # return letmeship tracking data api_id = frappe.db.get_single_value('LetMeShip','api_id') @@ -359,6 +348,7 @@ def get_letmeship_tracking_data(shipment_id): .format(tracking_data['message']) ) except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint( _('Error occurred while updating Shipment: {0}') .format(str(exc)), diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.py b/erpnext/erpnext_integrations/doctype/packlink/packlink.py index 7fdb053cf8..1db08c3149 100644 --- a/erpnext/erpnext_integrations/doctype/packlink/packlink.py +++ b/erpnext/erpnext_integrations/doctype/packlink/packlink.py @@ -72,6 +72,7 @@ def get_packlink_available_services(pickup_address, delivery_address, shipment_p return available_services except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint( _('Error occurred on Packlink: {0}') .format(str(exc)), indicator='orange', @@ -152,6 +153,7 @@ def create_packlink_shipment(pickup_address, delivery_address, shipment_parcel, 'awb_number': '', } except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint( _('Error occurred while creating Shipment: {0}') .format(str(exc)), @@ -215,6 +217,7 @@ def get_packlink_tracking_data(shipment_id): 'tracking_url': tracking_url } except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( str(exc)), indicator='orange', alert=True) return [] diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json index dab54cba6c..37b6898cba 100644 --- a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json +++ b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json @@ -24,7 +24,7 @@ }, { "fieldname": "api_secret", - "fieldtype": "Data", + "fieldtype": "Password", "label": "API Secret", "read_only_depends_on": "eval:doc.enabled == 0" } @@ -32,7 +32,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-08-18 09:48:50.836233", + "modified": "2020-10-21 10:28:57.710549", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "SendCloud", diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py index 85c94388dc..d30af15eb5 100644 --- a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py +++ b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py @@ -16,9 +16,7 @@ class SendCloud(Document): def get_sendcloud_available_services(delivery_address, shipment_parcel): # Retrieve rates at SendCloud from specification stated. - enabled = frappe.db.get_single_value('SendCloud', 'enabled') - api_key = frappe.db.get_single_value('SendCloud', 'api_key') - api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret']) if not enabled or not api_key or not api_secret: return [] @@ -40,6 +38,7 @@ def get_sendcloud_available_services(delivery_address, shipment_parcel): available_services.append(available_service) return available_services except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint(_('Error occurred on SendCloud: {0}').format( str(exc)), indicator='orange', alert=True) @@ -53,9 +52,7 @@ def create_sendcloud_shipment( value_of_goods ): # Create a transaction at SendCloud - enabled = frappe.db.get_single_value('SendCloud', 'enabled') - api_key = frappe.db.get_single_value('SendCloud', 'api_key') - api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') + api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret']) if not enabled or not api_key or not api_secret: return [] @@ -105,6 +102,7 @@ def create_sendcloud_shipment( 'awb_number': awb_number } except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint(_('Error occurred while creating Shipment: {0}').format( str(exc)), indicator='orange', alert=True) @@ -130,17 +128,15 @@ def get_sendcloud_tracking_data(shipment_id): api_key = frappe.db.get_single_value('SendCloud', 'api_key') api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') shipment_id_list = shipment_id.split(', ') - tracking_url = '' awb_number = [] tracking_status = [] tracking_status_info = [] + tracking_urls = [] for ship_id in shipment_id_list: tracking_data_response = \ requests.get('https://panel.sendcloud.sc/api/v2/parcels/{id}'.format(id=ship_id), auth=(api_key, api_secret)) tracking_data = json.loads(tracking_data_response.text) - tracking_url_template = \ - '{{ _("Click here to Track Shipment") }}
' - tracking_url += frappe.render_template(tracking_url_template, {'tracking_url': tracking_data['parcel']['tracking_url']}) + tracking_urls.append(tracking_data['parcel']['tracking_url']) awb_number.append(tracking_data['parcel']['tracking_number']) tracking_status.append(tracking_data['parcel']['status']['message']) tracking_status_info.append(tracking_data['parcel']['status']['message']) @@ -148,9 +144,10 @@ def get_sendcloud_tracking_data(shipment_id): 'awb_number': ', '.join(awb_number), 'tracking_status': ', '.join(tracking_status), 'tracking_status_info': ', '.join(tracking_status_info), - 'tracking_url': tracking_url + 'tracking_url': ', '.join(tracking_urls) } except Exception as exc: + frappe.log_error(frappe.get_traceback()) frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( str(exc)), indicator='orange', alert=True) diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py index e7ef4c8ebd..362f6cf88e 100644 --- a/erpnext/erpnext_integrations/utils.py +++ b/erpnext/erpnext_integrations/utils.py @@ -68,6 +68,4 @@ def get_tracking_url(carrier, tracking_number): url_reference = frappe.get_value('Parcel Service', carrier, 'url_reference') if url_reference: tracking_url = frappe.render_template(url_reference, {'tracking_number': tracking_number}) - tracking_url_template = '{{ _("Click here to Track Shipment") }}' - tracking_url = frappe.render_template(tracking_url_template, {'tracking_url': tracking_url}) return tracking_url diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 00a66fa48e..26e4f1633e 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -575,22 +575,24 @@ def make_shipment(source_name, target_doc=None): user = frappe.db.get_value("User", frappe.session.user, ['email', 'full_name', 'phone', 'mobile_no'], as_dict=1) target.pickup_contact_email = user.email pickup_contact_display = '{}'.format(user.full_name) - if user.email: - pickup_contact_display += '
' + user.email - if user.phone: - pickup_contact_display += '
' + user.phone - if user.mobile_no and not user.phone: - pickup_contact_display += '
' + user.mobile_no + if user: + if user.email: + pickup_contact_display += '
' + user.email + if user.phone: + pickup_contact_display += '
' + user.phone + if user.mobile_no and not user.phone: + pickup_contact_display += '
' + user.mobile_no target.pickup_contact = pickup_contact_display contact = frappe.db.get_value("Contact", source.contact_person, ['email_id', 'phone', 'mobile_no'], as_dict=1) delivery_contact_display = '{}'.format(source.contact_display) - if contact.email_id: - delivery_contact_display += '
' + contact.email_id - if contact.phone: - delivery_contact_display += '
' + contact.phone - if contact.mobile_no and not contact.phone: - delivery_contact_display += '
' + contact.mobile_no + if contact: + if contact.email_id: + delivery_contact_display += '
' + contact.email_id + if contact.phone: + delivery_contact_display += '
' + contact.phone + if contact.mobile_no and not contact.phone: + delivery_contact_display += '
' + contact.mobile_no target.delivery_contact = delivery_contact_display doclist = get_mapped_doc("Delivery Note", source_name, { @@ -612,7 +614,7 @@ def make_shipment(source_name, target_doc=None): } }, "Delivery Note Item": { - "doctype": "Shipment Delivery Notes", + "doctype": "Shipment Delivery Note", "field_map": { "name": "prevdoc_detail_docname", "parent": "prevdoc_docname", diff --git a/erpnext/stock/doctype/shipment/api/utils.py b/erpnext/stock/doctype/shipment/api/utils.py deleted file mode 100644 index 1153933e81..0000000000 --- a/erpnext/stock/doctype/shipment/api/utils.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- 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 import _ -import re - -def get_address(address_name): - address = frappe.db.get_value('Address', address_name, [ - 'address_title', - 'address_line1', - 'address_line2', - 'city', - 'pincode', - 'country', - ], as_dict=1) - address.country_code = frappe.db.get_value('Country', address.country, 'code').upper() - if not address.pincode or address.pincode == '': - frappe.throw(_("Postal Code is mandatory to continue.
\ - Please set Postal Code for Address {1}" - ).format(address_name, address_name)) - address.pincode = address.pincode.replace(' ', '') - address.city = address.city.strip() - return address - -def get_contact(contact_name): - contact = frappe.db.get_value('Contact', contact_name, [ - 'first_name', - 'last_name', - 'email_id', - 'phone', - 'mobile_no', - 'gender', - ], as_dict=1) - if not contact.last_name: - frappe.throw(_("Last Name is mandatory to continue.
\ - Please set Last Name for Contact {1}" - ).format(contact_name, contact_name)) - if not contact.phone: - contact.phone = contact.mobile_no - contact.phone_prefix = contact.phone[:3] - contact.phone = re.sub('[^A-Za-z0-9]+', '', contact.phone[3:]) - contact.email = contact.email_id - contact.title = 'MS' - if contact.gender == 'Male': - contact.title = 'MR' - return contact - -def get_company_contact(): - contact = frappe.db.get_value('User', frappe.session.user, [ - 'first_name', - 'last_name', - 'email', - 'phone', - 'mobile_no', - 'gender', - ], as_dict=1) - if not contact.phone: - contact.phone = contact.mobile_no - contact.phone_prefix = contact.phone[:3] - contact.phone = re.sub('[^A-Za-z0-9]+', '', contact.phone[3:]) - contact.title = 'MS' - if contact.gender == 'Male': - contact.title = 'MR' - return contact diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index e9f4484ab1..fc0b05f8af 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -2,11 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('Shipment', { - setup: function(frm) { - if (frm.doc.__islocal) { - frm.trigger('pickup_type'); - } - }, address_query: function(frm, link_doctype, link_name, is_your_company_address) { return { query: 'frappe.contacts.doctype.address.address.address_query', @@ -99,7 +94,7 @@ frappe.ui.form.on('Shipment', { } return frm.events.contact_query(frm, link_doctype, link_name); }); - frm.set_query("delivery_note", "shipment_delivery_notes", function() { + frm.set_query("delivery_note", "shipment_delivery_note", function() { let customer = ''; if (frm.doc.delivery_to_type == "Customer") { customer = frm.doc.delivery_customer; @@ -127,27 +122,22 @@ frappe.ui.form.on('Shipment', { if (frm.doc.shipment_id) { frm.add_custom_button(__('Print Shipping Label'), function() { return frm.events.print_shipping_label(frm); - }); + }, __('Tools')); if (frm.doc.tracking_status != 'Delivered') { frm.add_custom_button(__('Update Tracking'), function() { return frm.events.update_tracking(frm, frm.doc.service_provider, frm.doc.shipment_id); - }); + }, __('Tools')); + + frm.add_custom_button(__('Track Status'), function() { + const urls = frm.doc.tracking_url.split(', '); + urls.forEach(url => window.open(url)); + }, __('View')); } } $('div[data-fieldname=pickup_address] > div > .clearfix').hide(); $('div[data-fieldname=pickup_contact] > div > .clearfix').hide(); $('div[data-fieldname=delivery_address] > div > .clearfix').hide(); $('div[data-fieldname=delivery_contact] > div > .clearfix').hide(); - - if (frm.doc.delivery_from_type != 'Company') { - frm.set_df_property("delivery_contact_name", "reqd", 1); - } - if (frm.doc.pickup_from_type != 'Company') { - frm.set_df_property("pickup_contact_name", "reqd", 1); - } - else { - frm.toggle_display("pickup_contact_name", false); - } }, before_save: function(frm) { if (frm.doc.delivery_to_type == 'Company') { @@ -188,14 +178,10 @@ frappe.ui.form.on('Shipment', { pickup_from_type: function(frm) { if (frm.doc.pickup_from_type == 'Company') { frm.set_value("pickup_company", frappe.defaults.get_default('company')); - frm.set_df_property("pickup_contact_name", "reqd", 0); frm.set_value("pickup_customer", ''); frm.set_value("pickup_supplier", ''); - frm.toggle_display("pickup_contact_name", false); } else { - frm.set_df_property("pickup_contact_name", "reqd", 1); - frm.toggle_display("pickup_contact_name", true); frm.trigger('clear_pickup_fields'); } if (frm.doc.pickup_from_type == 'Customer') { @@ -206,20 +192,16 @@ frappe.ui.form.on('Shipment', { frm.set_value("pickup_customer", ''); frm.set_value("pickup_company", ''); } - frm.events.remove_notific_child_table(frm, 'shipment_notification_subscriptions', 'Pickup'); - frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscriptions', 'Pickup'); + frm.events.remove_notific_child_table(frm, 'shipment_notification_subscription', 'Pickup'); + frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscription', 'Pickup'); }, delivery_to_type: function(frm) { if (frm.doc.delivery_to_type == 'Company') { frm.set_value("delivery_company", frappe.defaults.get_default('company')); - frm.set_df_property("delivery_contact_name", "reqd", 0); frm.set_value("delivery_customer", ''); frm.set_value("delivery_supplier", ''); - frm.toggle_display("delivery_contact_name", false); } else { - frm.set_df_property("delivery_contact_name", "reqd", 1); - frm.toggle_display("delivery_contact_name", true); frm.trigger('clear_delivery_fields'); } if (frm.doc.delivery_to_type == 'Customer') { @@ -229,13 +211,13 @@ frappe.ui.form.on('Shipment', { if (frm.doc.delivery_to_type == 'Supplier') { frm.set_value("delivery_customer", ''); frm.set_value("delivery_company", ''); - frm.toggle_display("shipment_delivery_notes", false); + frm.toggle_display("shipment_delivery_note", false); } else { - frm.toggle_display("shipment_delivery_notes", true); + frm.toggle_display("shipment_delivery_note", true); } - frm.events.remove_notific_child_table(frm, 'shipment_notification_subscriptions', 'Delivery'); - frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscriptions', 'Delivery'); + frm.events.remove_notific_child_table(frm, 'shipment_notification_subscription', 'Delivery'); + frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscription', 'Delivery'); }, delivery_address_name: function(frm) { if (frm.doc.delivery_to_type == 'Company') { @@ -307,6 +289,51 @@ frappe.ui.form.on('Shipment', { frm.events.get_contact_display(frm, frm.doc.pickup_contact_name, 'Pickup'); } }, + pickup_contact_person: function(frm) { + if (frm.doc.pickup_contact_person) { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.get_company_contact", + args: { user: frm.doc.pickup_contact_person }, + callback: function({ message }) { + const r = message; + let contact_display = `${r.first_name} ${r.last_name}`; + if (r.email) { + contact_display += `
${ r.email }`; + frm.set_value('pickup_contact_email', r.email); + } + if (r.phone) { + contact_display += `
${ r.phone }`; + } + if (r.mobile_no && !r.phone) { + contact_display += `
${ r.mobile_no }`; + } + frm.set_value('pickup_contact', contact_display); + } + }); + } else { + if (frm.doc.pickup_from_type === 'Company') { + frappe.call({ + method: "erpnext.stock.doctype.shipment.shipment.get_company_contact", + args: { user: frappe.session.user }, + callback: function({ message }) { + const r = message; + let contact_display = `${r.first_name} ${r.last_name}`; + if (r.email) { + contact_display += `
${ r.email }`; + frm.set_value('pickup_contact_email', r.email); + } + if (r.phone) { + contact_display += `
${ r.phone }`; + } + if (r.mobile_no && !r.phone) { + contact_display += `
${ r.mobile_no }`; + } + frm.set_value('pickup_contact', contact_display); + } + }); + } + } + }, set_company_contact: function(frm, delivery_type) { frappe.db.get_value('User', { name: frappe.session.user }, ['full_name', 'last_name', 'email', 'phone', 'mobile_no'], (r) => { if (!(r.last_name && r.email && (r.phone || r.mobile_no))) { @@ -344,6 +371,7 @@ frappe.ui.form.on('Shipment', { } } }); + frm.set_value('pickup_contact_person', frappe.session.user); }, pickup_company: function(frm) { if (frm.doc.pickup_from_type == 'Company' && frm.doc.pickup_company) { @@ -372,14 +400,12 @@ frappe.ui.form.on('Shipment', { } }, pickup_customer: function(frm) { - frm.trigger('clear_pickup_fields'); if (frm.doc.pickup_customer) { frm.events.set_address_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); frm.events.set_contact_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); } }, pickup_supplier: function(frm) { - frm.trigger('clear_pickup_fields'); if (frm.doc.pickup_supplier) { frm.events.set_address_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); frm.events.set_contact_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); @@ -438,7 +464,7 @@ frappe.ui.form.on('Shipment', { }, pickup_date: function(frm) { if (frm.doc.pickup_date < frappe.datetime.get_today()) { - frappe.throw(__("Pickup Date cannot be in the past")); + frappe.throw(__("Pickup Date cannot be before this day")); } if (frm.doc.pickup_date == frappe.datetime.get_today()) { var pickup_time = frm.events.get_pickup_time(frm); @@ -487,65 +513,63 @@ frappe.ui.form.on('Shipment', { frm.set_value("pickup_to", pickup_to); }, clear_pickup_fields: function(frm) { - frm.set_value("pickup_address_name", ''); - frm.set_value("pickup_contact_name", ''); - frm.set_value("pickup_address", ''); - frm.set_value("pickup_contact", ''); - frm.set_value("pickup_contact_email", ''); + let fields = ["pickup_address_name", "pickup_contact_name", "pickup_address", "pickup_contact", "pickup_contact_email", "pickup_contact_person"]; + for (let field of fields){ + frm.set_value(field, ''); + } }, clear_delivery_fields: function(frm) { - frm.set_value("delivery_address_name", ''); - frm.set_value("delivery_contact_name", ''); - frm.set_value("delivery_address", ''); - frm.set_value("delivery_contact", ''); - frm.set_value("delivery_contact_email", ''); + let fields = ["delivery_address_name", "delivery_contact_name", "delivery_address", "delivery_contact", "delivery_contact_email"]; + for (let field of fields){ + frm.set_value(field, ''); + } }, pickup_from_send_shipping_notification: function(frm, cdt, cdn) { if (frm.doc.pickup_contact_email && frm.doc.pickup_from_send_shipping_notification - && !validate_duplicate(frm, 'shipment_notification_subscriptions', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscriptions", "shipment_notification_subscriptions"); + && !validate_duplicate(frm, 'shipment_notification_subscription', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscription", "shipment_notification_subscription"); row.email = frm.doc.pickup_contact_email; - frm.refresh_fields("shipment_notification_subscriptions"); + frm.refresh_fields("shipment_notification_subscription"); } if (!frm.doc.pickup_from_send_shipping_notification) { - frm.events.remove_email_row(frm, 'shipment_notification_subscriptions', frm.doc.pickup_contact_email); - frm.refresh_fields("shipment_notification_subscriptions"); + frm.events.remove_email_row(frm, 'shipment_notification_subscription', frm.doc.pickup_contact_email); + frm.refresh_fields("shipment_notification_subscription"); } }, pickup_from_subscribe_to_status_updates: function(frm, cdt, cdn) { if (frm.doc.pickup_contact_email && frm.doc.pickup_from_subscribe_to_status_updates - && !validate_duplicate(frm, 'shipment_status_update_subscriptions', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscriptions", "shipment_status_update_subscriptions"); + && !validate_duplicate(frm, 'shipment_status_update_subscription', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscription", "shipment_status_update_subscription"); row.email = frm.doc.pickup_contact_email; - frm.refresh_fields("shipment_status_update_subscriptions"); + frm.refresh_fields("shipment_status_update_subscription"); } if (!frm.doc.pickup_from_subscribe_to_status_updates) { - frm.events.remove_email_row(frm, 'shipment_status_update_subscriptions', frm.doc.pickup_contact_email); - frm.refresh_fields("shipment_status_update_subscriptions"); + frm.events.remove_email_row(frm, 'shipment_status_update_subscription', frm.doc.pickup_contact_email); + frm.refresh_fields("shipment_status_update_subscription"); } }, delivery_to_send_shipping_notification: function(frm, cdt, cdn) { if (frm.doc.delivery_contact_email && frm.doc.delivery_to_send_shipping_notification - && !validate_duplicate(frm, 'shipment_notification_subscriptions', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)){ - let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscriptions", "shipment_notification_subscriptions"); + && !validate_duplicate(frm, 'shipment_notification_subscription', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)){ + let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscription", "shipment_notification_subscription"); row.email = frm.doc.delivery_contact_email; - frm.refresh_fields("shipment_notification_subscriptions"); + frm.refresh_fields("shipment_notification_subscription"); } if (!frm.doc.delivery_to_send_shipping_notification) { - frm.events.remove_email_row(frm, 'shipment_notification_subscriptions', frm.doc.delivery_contact_email); - frm.refresh_fields("shipment_notification_subscriptions"); + frm.events.remove_email_row(frm, 'shipment_notification_subscription', frm.doc.delivery_contact_email); + frm.refresh_fields("shipment_notification_subscription"); } }, delivery_to_subscribe_to_status_updates: function(frm, cdt, cdn) { if (frm.doc.delivery_contact_email && frm.doc.delivery_to_subscribe_to_status_updates - && !validate_duplicate(frm, 'shipment_status_update_subscriptions', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscriptions", "shipment_status_update_subscriptions"); + && !validate_duplicate(frm, 'shipment_status_update_subscription', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)) { + let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscription", "shipment_status_update_subscription"); row.email = frm.doc.delivery_contact_email; - frm.refresh_fields("shipment_status_update_subscriptions"); + frm.refresh_fields("shipment_status_update_subscription"); } if (!frm.doc.delivery_to_subscribe_to_status_updates) { - frm.events.remove_email_row(frm, 'shipment_status_update_subscriptions', frm.doc.delivery_contact_email); - frm.refresh_fields("shipment_status_update_subscriptions"); + frm.events.remove_email_row(frm, 'shipment_status_update_subscription', frm.doc.delivery_contact_email); + frm.refresh_fields("shipment_status_update_subscription"); } }, remove_email_row: function(frm, table, fieldname) { @@ -589,7 +613,7 @@ frappe.ui.form.on('Shipment', { shipment_parcel: frm.doc.shipment_parcel, description_of_content: frm.doc.description_of_content, pickup_date: frm.doc.pickup_date, - pickup_contact_name: frm.doc.pickup_contact_name, + pickup_contact_name: frm.doc.pickup_from_type === 'Company' ? frm.doc.pickup_contact_person : frm.doc.pickup_contact_name, delivery_contact_name: frm.doc.delivery_contact_name, value_of_goods: frm.doc.value_of_goods }, @@ -639,7 +663,7 @@ frappe.ui.form.on('Shipment', { }, update_tracking: function(frm, service_provider, shipment_id) { let delivery_notes = []; - (frm.doc.shipment_delivery_notes || []).forEach((d) => { + (frm.doc.shipment_delivery_note || []).forEach((d) => { delivery_notes.push(d.delivery_note); }); frappe.call({ @@ -661,14 +685,13 @@ frappe.ui.form.on('Shipment', { } }); -frappe.ui.form.on('Shipment Delivery Notes', { +frappe.ui.form.on('Shipment Delivery Note', { delivery_note: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.delivery_note) { let row_index = row.idx - 1; - if(validate_duplicate(frm, 'shipment_delivery_notes', row.delivery_note, row_index)) { - cur_frm.get_field('shipment_delivery_notes').grid.grid_rows[row_index].remove(); - frappe.throw(__(`You have entered duplicate Delivery Notes. Please rectify and try again.`)); + if(validate_duplicate(frm, 'shipment_delivery_note', row.delivery_note, row_index)) { + frappe.throw(__(`You have entered a duplicate Delivery Note on Row ${row.idx}. Please rectify and try again.`)); } } }, @@ -683,29 +706,27 @@ frappe.ui.form.on('Shipment Delivery Notes', { }); var validate_duplicate = function(frm, table, fieldname, index){ - let duplicate = false; - $.each(frm.doc[table], function(i, detail) { - // Email duplicate validation - if(detail.email === fieldname && !(index === i)) { - duplicate = true; - return; - } - - // Delivery Note duplicate validation - if(detail.delivery_note === fieldname && !(index === i)) { - duplicate = true; - return; - } - }); - return duplicate; + return ( + table === 'shipment_delivery_note' + ? frm.doc[table].some((detail, i) => detail.delivery_note === fieldname && !(index === i)) + : frm.doc[table].some((detail, i) => detail.email === fieldname && !(index === i)) + ); }; function select_from_available_services(frm, available_services) { var headers = [ __("Service Provider"), __("Carrier"), __("Carrier’s Service"), __("Price"), "" ]; cur_frm.render_available_services = function(d, headers, data){ + const arranged_data = data.reduce((prev, curr) => { + if (curr.is_preferred) { + prev.preferred_services.push(curr); + } else { + prev.other_services.push(curr); + } + return prev; + }, { preferred_services: [], other_services: [] }); d.fields_dict.available_services.$wrapper.html( frappe.render_template('shipment_service_selector', - {'header_columns': headers, 'data': data} + {'header_columns': headers, 'data': arranged_data} ) ); }; @@ -722,18 +743,18 @@ function select_from_available_services(frm, available_services) { cur_frm.render_available_services(d, headers, available_services); let shipment_notific_email = []; let tracking_notific_email = []; - (frm.doc.shipment_notification_subscriptions || []).forEach((d) => { + (frm.doc.shipment_notification_subscription || []).forEach((d) => { if (!d.unsubscribed) { shipment_notific_email.push(d.email); } }); - (frm.doc.shipment_status_update_subscriptions || []).forEach((d) => { + (frm.doc.shipment_status_update_subscription || []).forEach((d) => { if (!d.unsubscribed) { tracking_notific_email.push(d.email); } }); let delivery_notes = []; - (frm.doc.shipment_delivery_notes || []).forEach((d) => { + (frm.doc.shipment_delivery_note || []).forEach((d) => { delivery_notes.push(d.delivery_note); }); cur_frm.select_row = function(service_data){ @@ -750,7 +771,7 @@ function select_from_available_services(frm, available_services) { shipment_parcel: frm.doc.shipment_parcel, description_of_content: frm.doc.description_of_content, pickup_date: frm.doc.pickup_date, - pickup_contact_name: frm.doc.pickup_contact_name, + pickup_contact_name: frm.doc.pickup_from_type === 'Company' ? frm.doc.pickup_contact_person : frm.doc.pickup_contact_name, delivery_contact_name: frm.doc.delivery_contact_name, value_of_goods: frm.doc.value_of_goods, service_data: service_data, diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index b6656a2b72..bbfbb719be 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -14,6 +14,7 @@ "pickup", "pickup_address_name", "pickup_address", + "pickup_contact_person", "pickup_contact_name", "pickup_contact_email", "pickup_contact", @@ -32,17 +33,17 @@ "notification_details_section", "pickup_from_send_shipping_notification", "pickup_from_subscribe_to_status_updates", - "shipment_notification_subscriptions", + "shipment_notification_subscription", "column_break_27", "delivery_to_send_shipping_notification", "delivery_to_subscribe_to_status_updates", - "shipment_status_update_subscriptions", + "shipment_status_update_subscription", "parcels_section", "shipment_parcel", "parcel_template", "add_template", "column_break_28", - "shipment_delivery_notes", + "shipment_delivery_note", "shipment_details_section", "pallets", "value_of_goods", @@ -125,10 +126,11 @@ "read_only": 1 }, { - "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type == \"Company\"", + "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type !== \"Company\"", "fieldname": "pickup_contact_name", "fieldtype": "Link", "label": "Contact", + "mandatory_depends_on": "eval: doc.pickup_from_type !== 'Company'", "options": "Contact" }, { @@ -206,6 +208,7 @@ "fieldname": "delivery_contact_name", "fieldtype": "Link", "label": "Contact", + "mandatory_depends_on": "eval: doc.delivery_from_type !== 'Company'", "options": "Contact" }, { @@ -239,12 +242,6 @@ "fieldtype": "Check", "label": "Subscribe to status updates" }, - { - "fieldname": "shipment_notification_subscriptions", - "fieldtype": "Table", - "label": "Shipment Notification Subscriptions", - "options": "Shipment Notification Subscriptions" - }, { "fieldname": "column_break_27", "fieldtype": "Column Break" @@ -261,12 +258,6 @@ "fieldtype": "Check", "label": "Subscribe to status updates" }, - { - "fieldname": "shipment_status_update_subscriptions", - "fieldtype": "Table", - "label": "Shipment Status Update Subscriptions", - "options": "Shipment Status Update Subscriptions" - }, { "fieldname": "parcels_section", "fieldtype": "Section Break", @@ -293,12 +284,6 @@ "fieldname": "column_break_28", "fieldtype": "Column Break" }, - { - "fieldname": "shipment_delivery_notes", - "fieldtype": "Table", - "label": "Shipment Delivery Notes", - "options": "Shipment Delivery Notes" - }, { "fieldname": "shipment_details_section", "fieldtype": "Section Break", @@ -328,16 +313,14 @@ { "default": "09:00", "fieldname": "pickup_from", - "fieldtype": "Select", - "label": "Pickup from", - "options": "09:00\n09:30\n10:00\n10:30\n11:00\n11:30\n12:00\n12:30\n13:00\n13:30\n14:00\n14:30\n15:00\n15:30\n16:00\n16:30\n17:00\n17:30\n18:00\n18:30\n19:00" + "fieldtype": "Time", + "label": "Pickup from" }, { "default": "17:00", "fieldname": "pickup_to", - "fieldtype": "Select", - "label": "Pickup to", - "options": "09:00\n09:30\n10:00\n10:30\n11:00\n11:30\n12:00\n12:30\n13:00\n13:30\n14:00\n14:30\n15:00\n15:30\n16:00\n16:30\n17:00\n17:30\n18:00\n18:30\n19:00" + "fieldtype": "Time", + "label": "Pickup to" }, { "fieldname": "column_break_36", @@ -374,13 +357,15 @@ }, { "fieldname": "service_provider", - "fieldtype": "Read Only", - "label": "Service Provider" + "fieldtype": "Data", + "label": "Service Provider", + "read_only": 1 }, { "fieldname": "shipment_id", - "fieldtype": "Read Only", - "label": "Shipment ID" + "fieldtype": "Data", + "label": "Shipment ID", + "read_only": 1 }, { "fieldname": "shipment_amount", @@ -399,23 +384,27 @@ { "fieldname": "tracking_url", "fieldtype": "Small Text", + "hidden": 1, "label": "Tracking URL", "read_only": 1 }, { "fieldname": "carrier", - "fieldtype": "Read Only", - "label": "Carrier" + "fieldtype": "Data", + "label": "Carrier", + "read_only": 1 }, { "fieldname": "carrier_service", - "fieldtype": "Read Only", - "label": "Carrier Service" + "fieldtype": "Data", + "label": "Carrier Service", + "read_only": 1 }, { "fieldname": "awb_number", - "fieldtype": "Read Only", - "label": "AWB Number" + "fieldtype": "Data", + "label": "AWB Number", + "read_only": 1 }, { "fieldname": "tracking_status", @@ -449,11 +438,37 @@ "fieldtype": "Select", "label": "Incoterm", "options": "EXW (Ex Works)\nFCA (Free Carrier)\nCPT (Carriage Paid To)\nCIP (Carriage and Insurance Paid to)\nDPU (Delivered At Place Unloaded)\nDAP (Delivered At Place)\nDDP (Delivered Duty Paid)" + }, + { + "fieldname": "shipment_delivery_note", + "fieldtype": "Table", + "label": "Shipment Delivery Note", + "options": "Shipment Delivery Note" + }, + { + "fieldname": "shipment_notification_subscription", + "fieldtype": "Table", + "label": "Shipment Notification Subscription", + "options": "Shipment Notification Subscription" + }, + { + "fieldname": "shipment_status_update_subscription", + "fieldtype": "Table", + "label": "Shipment Status Update Subscription", + "options": "Shipment Status Update Subscription" + }, + { + "depends_on": "eval:doc.pickup_from_type === 'Company'", + "fieldname": "pickup_contact_person", + "fieldtype": "Link", + "label": "Pickup Contact Person", + "mandatory_depends_on": "eval:doc.pickup_from_type === 'Company'", + "options": "User" } ], "is_submittable": 1, "links": [], - "modified": "2020-07-24 11:44:30.904612", + "modified": "2020-09-29 13:59:50.241744", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index e059bacfa1..9b3c976ca4 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import json from frappe import _ +from frappe.utils import flt from frappe.model.document import Document from erpnext.accounts.party import get_party_shipping_address from frappe.contacts.doctype.contact.contact import get_default_contact @@ -32,7 +33,7 @@ class Shipment(Document): def validate_weight(self): for parcel in self.shipment_parcel: - if parcel.weight <= 0: + if flt(parcel.weight) <= 0: frappe.throw(_('Parcel weight cannot be 0')) @frappe.whitelist() @@ -52,12 +53,12 @@ def fetch_shipping_rates(pickup_from_type, delivery_to_type, pickup_address_name if pickup_from_type != 'Company': pickup_contact = get_contact(pickup_contact_name) else: - pickup_contact = get_company_contact() + pickup_contact = get_company_contact(user=pickup_contact_name) if delivery_to_type != 'Company': delivery_contact = get_contact(delivery_contact_name) else: - delivery_contact = get_company_contact() + delivery_contact = get_company_contact(user=pickup_contact_name) letmeship_prices = get_letmeship_available_services( delivery_to_type=delivery_to_type, pickup_address=pickup_address, @@ -104,12 +105,12 @@ def create_shipment(shipment, pickup_from_type, delivery_to_type, pickup_address if pickup_from_type != 'Company': pickup_contact = get_contact(pickup_contact_name) else: - pickup_contact = get_company_contact() + pickup_contact = get_company_contact(user=pickup_contact_name) if delivery_to_type != 'Company': delivery_contact = get_contact(delivery_contact_name) else: - delivery_contact = get_company_contact() + delivery_contact = get_company_contact(user=pickup_contact_name) if service_info['service_provider'] == LETMESHIP_PROVIDER: shipment_info = create_letmeship_shipment( pickup_address=pickup_address, @@ -150,12 +151,9 @@ def create_shipment(shipment, pickup_from_type, delivery_to_type, pickup_address ) if shipment_info: - frappe.db.set_value('Shipment', shipment, 'service_provider', shipment_info.get('service_provider')) - frappe.db.set_value('Shipment', shipment, 'carrier', shipment_info.get('carrier')) - frappe.db.set_value('Shipment', shipment, 'carrier_service', shipment_info.get('carrier_service')) - frappe.db.set_value('Shipment', shipment, 'shipment_id', shipment_info.get('shipment_id')) - frappe.db.set_value('Shipment', shipment, 'shipment_amount', shipment_info.get('shipment_amount')) - frappe.db.set_value('Shipment', shipment, 'awb_number', shipment_info.get('awb_number')) + fields = ['service_provider', 'carrier', 'carrier_service', 'shipment_id', 'shipment_amount', 'awb_number'] + for field in fields: + frappe.db.set_value('Shipment', shipment, field, shipment_info.get(field)) frappe.db.set_value('Shipment', shipment, 'status', 'Booked') if delivery_notes: update_delivery_note(delivery_notes=delivery_notes, shipment_info=shipment_info) @@ -277,9 +275,17 @@ def get_contact(contact_name): contact.phone = contact.mobile_no return contact +def match_parcel_service_type_carrier(shipment_prices, reference): + for idx, prices in enumerate(shipment_prices): + service_name = match_parcel_service_type_alias(prices.get(reference[0]), prices.get(reference[1])) + is_preferred = frappe.db.get_value('Parcel Service Type', service_name, 'show_in_preferred_services_list') + shipment_prices[idx].service_name = service_name + shipment_prices[idx].is_preferred = is_preferred + return shipment_prices -def get_company_contact(): - contact = frappe.db.get_value('User', frappe.session.user, [ +@frappe.whitelist() +def get_company_contact(user): + contact = frappe.db.get_value('User', user, [ 'first_name', 'last_name', 'email', @@ -289,12 +295,4 @@ def get_company_contact(): ], as_dict=1) if not contact.phone: contact.phone = contact.mobile_no - return contact - -def match_parcel_service_type_carrier(shipment_prices, reference): - for idx, prices in enumerate(shipment_prices): - service_name = match_parcel_service_type_alias(prices.get(reference[0]), prices.get(reference[1])) - is_preferred = frappe.db.get_value('Parcel Service Type', service_name, 'show_in_preferred_services_list') - shipment_prices[idx].service_name = service_name - shipment_prices[idx].is_preferred = is_preferred - return shipment_prices + return contact \ No newline at end of file diff --git a/erpnext/stock/doctype/shipment/shipment_service_selector.html b/erpnext/stock/doctype/shipment/shipment_service_selector.html index ed9b8bf400..4ccbe34e9e 100644 --- a/erpnext/stock/doctype/shipment/shipment_service_selector.html +++ b/erpnext/stock/doctype/shipment/shipment_service_selector.html @@ -1,59 +1,63 @@ -{% if (data.length) { %} -
+{% if (data.preferred_services.length || data.other_services.length) { %} +
{{ __("Preferred Services") }}
- - - - {% for (var i = 0; i < header_columns.length; i++) { %} - - {% } %} - - - - {% for (var i = 0; i < data.length; i++) { %} - {% if (data[i].is_preferred) { %} + {% if (data.preferred_services.length) { %} +
{{ header_columns[i] }}
+ + + {% for (var i = 0; i < header_columns.length; i++) { %} + + {% } %} + + + + {% for (var i = 0; i < data.preferred_services.length; i++) { %} - - - - + + + + {% } %} - {% } %} - -
{{ header_columns[i] }}
{{ data[i].service_provider }}{{ data[i].carrier }}{{ data[i].service_name }}{{ format_currency(data[i].total_price, 'EUR', 2) }}{{ data.preferred_services[i].service_provider }}{{ data.preferred_services[i].carrier }}{{ data.preferred_services[i].service_name }}{{ format_currency(data.preferred_services[i].total_price, 'EUR', 2) }} -
+ + + {% } else { %} +
{{ __("No Preferred Services Available") }}
+ {% } %}
{{ __("Other Services") }}
- - - - {% for (var i = 0; i < header_columns.length; i++) { %} - - {% } %} - - - - {% for (var i = 0; i < data.length; i++) { %} - {% if (!data[i].is_preferred) { %} + {% if (data.other_services.length) { %} +
{{ header_columns[i] }}
+ + + {% for (var i = 0; i < header_columns.length; i++) { %} + + {% } %} + + + + {% for (var i = 0; i < data.other_services.length; i++) { %} - - - - + + + + {% } %} - {% } %} - -
{{ header_columns[i] }}
{{ data[i].service_provider }}{{ data[i].carrier }}{{ data[i].service_name }}{{ format_currency(data[i].total_price, 'EUR', 2) }}{{ data.other_services[i].service_provider }}{{ data.other_services[i].carrier }}{{ data.other_services[i].service_name }}{{ format_currency(data.other_services[i].total_price, 'EUR', 2) }} -
+ + + {% } else { %} +
{{ __("No Services Available") }}
+ {% } %}
{% } else { %}
{{ __("No Services Available") }}
diff --git a/erpnext/stock/doctype/shipment_delivery_notes/__init__.py b/erpnext/stock/doctype/shipment_delivery_note/__init__.py similarity index 100% rename from erpnext/stock/doctype/shipment_delivery_notes/__init__.py rename to erpnext/stock/doctype/shipment_delivery_note/__init__.py diff --git a/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json similarity index 95% rename from erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json rename to erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json index fbc01d9a24..9651e3f945 100644 --- a/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.json +++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json @@ -31,7 +31,7 @@ "modified": "2020-07-09 12:55:01.134270", "modified_by": "Administrator", "module": "Stock", - "name": "Shipment Delivery Notes", + "name": "Shipment Delivery Note", "owner": "Administrator", "permissions": [], "quick_entry": 1, diff --git a/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.py b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py similarity index 86% rename from erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.py rename to erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py index ed936c60f8..4342151605 100644 --- a/erpnext/stock/doctype/shipment_delivery_notes/shipment_delivery_notes.py +++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class ShipmentDeliveryNotes(Document): +class ShipmentDeliveryNote(Document): pass diff --git a/erpnext/stock/doctype/shipment_notification_subscriptions/__init__.py b/erpnext/stock/doctype/shipment_notification_subscription/__init__.py similarity index 100% rename from erpnext/stock/doctype/shipment_notification_subscriptions/__init__.py rename to erpnext/stock/doctype/shipment_notification_subscription/__init__.py diff --git a/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json similarity index 93% rename from erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json rename to erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json index bd9b8003a8..d927d9902e 100644 --- a/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.json +++ b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json @@ -30,7 +30,7 @@ "modified": "2020-07-09 12:55:14.217387", "modified_by": "Administrator", "module": "Stock", - "name": "Shipment Notification Subscriptions", + "name": "Shipment Notification Subscription", "owner": "Administrator", "permissions": [], "quick_entry": 1, diff --git a/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.py b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py similarity index 83% rename from erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.py rename to erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py index 28ead7fab8..c816e4343c 100644 --- a/erpnext/stock/doctype/shipment_notification_subscriptions/shipment_notification_subscriptions.py +++ b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class ShipmentNotificationSubscriptions(Document): +class ShipmentNotificationSubscription(Document): pass diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json index ec2bb1c9b3..4735d9f886 100644 --- a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json +++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json @@ -1,26 +1,18 @@ { "actions": [], - "autoname": "field:preset_name", + "autoname": "field:parcel_template_name", "creation": "2020-07-09 11:43:43.470339", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "preset_name", + "parcel_template_name", "length", "width", "height", "weight" ], "fields": [ - { - "fieldname": "preset_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Preset Name", - "reqd": 1, - "unique": 1 - }, { "fieldname": "length", "fieldtype": "Int", @@ -49,10 +41,18 @@ "label": "Weight (kg)", "precision": "1", "reqd": 1 + }, + { + "fieldname": "parcel_template_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parcel Template Name", + "reqd": 1, + "unique": 1 } ], "links": [], - "modified": "2020-07-10 12:53:22.772826", + "modified": "2020-09-28 12:51:00.320421", "modified_by": "Administrator", "module": "Stock", "name": "Shipment Parcel Template", diff --git a/erpnext/stock/doctype/shipment_status_update_subscriptions/__init__.py b/erpnext/stock/doctype/shipment_status_update_subscription/__init__.py similarity index 100% rename from erpnext/stock/doctype/shipment_status_update_subscriptions/__init__.py rename to erpnext/stock/doctype/shipment_status_update_subscription/__init__.py diff --git a/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json similarity index 93% rename from erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json rename to erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json index 3b86b400d8..a7fe4a4a0a 100644 --- a/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.json +++ b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json @@ -30,7 +30,7 @@ "modified": "2020-07-09 12:55:27.615463", "modified_by": "Administrator", "module": "Stock", - "name": "Shipment Status Update Subscriptions", + "name": "Shipment Status Update Subscription", "owner": "Administrator", "permissions": [], "quick_entry": 1, diff --git a/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py similarity index 83% rename from erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py rename to erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py index a8e31ea778..1b006d7efc 100644 --- a/erpnext/stock/doctype/shipment_status_update_subscriptions/shipment_status_update_subscriptions.py +++ b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe from frappe.model.document import Document -class ShipmentStatusUpdateSubscriptions(Document): +class ShipmentStatusUpdateSubscription(Document): pass From ac3c1f14938655ed0c01315c8ca675d1fbb442aa Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Fri, 20 Nov 2020 08:12:29 +0000 Subject: [PATCH 204/283] chore: remove packlink, letmeship, and sendcloud files --- .../doctype/letmeship/__init__.py | 0 .../doctype/letmeship/letmeship.js | 8 - .../doctype/letmeship/letmeship.json | 55 --- .../doctype/letmeship/letmeship.py | 386 ------------------ .../doctype/letmeship/test_letmeship.py | 10 - .../doctype/packlink/__init__.py | 0 .../doctype/packlink/packlink.js | 8 - .../doctype/packlink/packlink.json | 48 --- .../doctype/packlink/packlink.py | 240 ----------- .../doctype/packlink/test_packlink.py | 10 - .../doctype/sendcloud/__init__.py | 0 .../doctype/sendcloud/sendcloud.js | 8 - .../doctype/sendcloud/sendcloud.json | 56 --- .../doctype/sendcloud/sendcloud.py | 168 -------- .../doctype/sendcloud/test_sendcloud.py | 10 - .../stock/doctype/parcel_service/__init__.py | 0 .../doctype/parcel_service/parcel_service.js | 8 - .../parcel_service/parcel_service.json | 56 --- .../doctype/parcel_service/parcel_service.py | 10 - .../parcel_service/test_parcel_service.py | 10 - .../doctype/parcel_service_type/__init__.py | 0 .../parcel_service_type.js | 12 - .../parcel_service_type.json | 89 ---- .../parcel_service_type.py | 22 - .../test_parcel_service_type.py | 10 - .../parcel_service_type_alias/__init__.py | 0 .../parcel_service_type_alias.json | 41 -- .../parcel_service_type_alias.py | 10 - erpnext/stock/doctype/shipment/shipment.js | 183 --------- erpnext/stock/doctype/shipment/shipment.json | 26 +- erpnext/stock/doctype/shipment/shipment.py | 243 +---------- .../shipment/shipment_service_selector.html | 74 ---- 32 files changed, 10 insertions(+), 1791 deletions(-) delete mode 100644 erpnext/erpnext_integrations/doctype/letmeship/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.js delete mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.json delete mode 100644 erpnext/erpnext_integrations/doctype/letmeship/letmeship.py delete mode 100644 erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py delete mode 100644 erpnext/erpnext_integrations/doctype/packlink/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.js delete mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.json delete mode 100644 erpnext/erpnext_integrations/doctype/packlink/packlink.py delete mode 100644 erpnext/erpnext_integrations/doctype/packlink/test_packlink.py delete mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js delete mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json delete mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py delete mode 100644 erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py delete mode 100644 erpnext/stock/doctype/parcel_service/__init__.py delete mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.js delete mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.json delete mode 100644 erpnext/stock/doctype/parcel_service/parcel_service.py delete mode 100644 erpnext/stock/doctype/parcel_service/test_parcel_service.py delete mode 100644 erpnext/stock/doctype/parcel_service_type/__init__.py delete mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.js delete mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.json delete mode 100644 erpnext/stock/doctype/parcel_service_type/parcel_service_type.py delete mode 100644 erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py delete mode 100644 erpnext/stock/doctype/parcel_service_type_alias/__init__.py delete mode 100644 erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json delete mode 100644 erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.py delete mode 100644 erpnext/stock/doctype/shipment/shipment_service_selector.html diff --git a/erpnext/erpnext_integrations/doctype/letmeship/__init__.py b/erpnext/erpnext_integrations/doctype/letmeship/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.js b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.js deleted file mode 100644 index 1e5e372dff..0000000000 --- a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('LetMeShip', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json deleted file mode 100644 index 94b001ed08..0000000000 --- a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "actions": [], - "creation": "2020-07-23 10:55:19.669830", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enabled", - "api_id", - "api_password" - ], - "fields": [ - { - "default": "0", - "fieldname": "enabled", - "fieldtype": "Check", - "label": "Enabled" - }, - { - "fieldname": "api_id", - "fieldtype": "Data", - "label": "API ID", - "read_only_depends_on": "eval:doc.enabled == 0" - }, - { - "fieldname": "api_password", - "fieldtype": "Password", - "label": "API Password", - "read_only_depends_on": "eval:doc.enabled == 0" - } - ], - "issingle": 1, - "links": [], - "modified": "2020-10-21 10:28:37.607717", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "LetMeShip", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py b/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py deleted file mode 100644 index 162c1edb37..0000000000 --- a/erpnext/erpnext_integrations/doctype/letmeship/letmeship.py +++ /dev/null @@ -1,386 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import requests -import frappe -import json -import re -from frappe import _ -from frappe.model.document import Document -from erpnext.erpnext_integrations.utils import get_tracking_url - -LETMESHIP_PROVIDER = 'LetMeShip' - -class LetMeShip(Document): - pass - -def get_letmeship_available_services(delivery_to_type, pickup_address, - delivery_address, shipment_parcel, description_of_content, pickup_date, - value_of_goods, pickup_contact=None, delivery_contact=None): - # Retrieve rates at LetMeShip from specification stated. - api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id']) - if not enabled or not api_id or not api_password: - return [] - - set_letmeship_specific_fields(pickup_contact, delivery_contact) - - # LetMeShip have limit of 30 characters for Company field - if len(pickup_address.address_title) > 30: - pickup_address.address_title = pickup_address.address_title[:30] - if len(delivery_address.address_title) > 30: - delivery_address.address_title = delivery_address.address_title[:30] - parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content) - - url = 'https://api.letmeship.com/v1/available' - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Origin': 'string' - } - payload = generate_payload( - pickup_address=pickup_address, - pickup_contact=pickup_contact, - delivery_address=delivery_address, - delivery_contact=delivery_contact, - description_of_content=description_of_content, - value_of_goods=value_of_goods, - parcel_list=parcel_list, - pickup_date=pickup_date - ) - try: - available_services = [] - response_data = requests.post( - url=url, - auth=(api_id, api_password), - headers=headers, - data=json.dumps(payload) - ) - response_data = json.loads(response_data.text) - if 'serviceList' in response_data: - for response in response_data['serviceList']: - available_service = frappe._dict() - basic_info = response['baseServiceDetails'] - price_info = basic_info['priceInfo'] - available_service.service_provider = LETMESHIP_PROVIDER - available_service.id = basic_info['id'] - available_service.carrier = basic_info['carrier'] - available_service.carrier_name = basic_info['name'] - available_service.service_name = '' - available_service.is_preferred = 0 - available_service.real_weight = price_info['realWeight'] - available_service.total_price = price_info['netPrice'] - available_service.price_info = price_info - available_services.append(available_service) - return available_services - else: - frappe.throw( - _('Error occurred while fetching LetMeShip prices: {0}') - .format(response_data['message']) - ) - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred while fetching LetMeShip Prices: {0}') - .format(str(exc)), - indicator='orange', - alert=True - ) - return [] - - -def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, description_of_content, - pickup_date, value_of_goods, service_info, shipment_notific_email, tracking_notific_email, - pickup_contact=None, delivery_contact=None): - # Create a transaction at LetMeShip - # LetMeShip have limit of 30 characters for Company field - api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id']) - if not enabled or not api_id or not api_password: - return [] - - set_letmeship_specific_fields(pickup_contact, delivery_contact) - - if len(pickup_address.address_title) > 30: - pickup_address.address_title = pickup_address.address_title[:30] - if len(delivery_address.address_title) > 30: - delivery_address.address_title = delivery_address.address_title[:30] - - parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content) - url = 'https://api.letmeship.com/v1/shipments' - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Origin': 'string' - } - payload = generate_payload( - pickup_address=pickup_address, - pickup_contact=pickup_contact, - delivery_address=delivery_address, - delivery_contact=delivery_contact, - description_of_content=description_of_content, - value_of_goods=value_of_goods, - parcel_list=parcel_list, - pickup_date=pickup_date, - service_info=service_info, - tracking_notific_email=tracking_notific_email, - shipment_notific_email=shipment_notific_email - ) - try: - response_data = requests.post( - url=url, - auth=(api_id, api_password), - headers=headers, - data=json.dumps(payload) - ) - response_data = json.loads(response_data.text) - if 'shipmentId' in response_data: - shipment_amount = response_data['service']['priceInfo']['totalPrice'] - awb_number = '' - url = 'https://api.letmeship.com/v1/shipments/{id}'.format(id=response_data['shipmentId']) - tracking_response = requests.get(url, auth=(api_id, api_password),headers=headers) - tracking_response_data = json.loads(tracking_response.text) - if 'trackingData' in tracking_response_data: - for parcel in tracking_response_data['trackingData']['parcelList']: - if 'awbNumber' in parcel: - awb_number = parcel['awbNumber'] - return { - 'service_provider': LETMESHIP_PROVIDER, - 'shipment_id': response_data['shipmentId'], - 'carrier': service_info['carrier'], - 'carrier_service': service_info['service_name'], - 'shipment_amount': shipment_amount, - 'awb_number': awb_number, - } - elif 'message' in response_data: - frappe.throw( - _('Error occurred while creating Shipment: {0}') - .format(response_data['message']) - ) - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred while creating Shipment: {0}') - .format(str(exc)), - indicator='orange', - alert=True - ) - -def generate_payload( - pickup_address, - pickup_contact, - delivery_address, - delivery_contact, - description_of_content, - value_of_goods, - parcel_list, - pickup_date, - service_info=None, - tracking_notific_email=None, - shipment_notific_email=None -): - payload = { - 'pickupInfo': { - 'address': { - 'countryCode': pickup_address.country_code, - 'zip': pickup_address.pincode, - 'city': pickup_address.city, - 'street': pickup_address.address_line1, - 'addressInfo1': pickup_address.address_line2, - 'houseNo': '', - }, - 'company': pickup_address.address_title, - 'person': { - 'title': pickup_contact.title, - 'firstname': pickup_contact.first_name, - 'lastname': pickup_contact.last_name - }, - 'phone': { - 'phoneNumber': pickup_contact.phone, - 'phoneNumberPrefix': pickup_contact.phone_prefix - }, - 'email': pickup_contact.email, - }, - 'deliveryInfo': { - 'address': { - 'countryCode': delivery_address.country_code, - 'zip': delivery_address.pincode, - 'city': delivery_address.city, - 'street': delivery_address.address_line1, - 'addressInfo1': delivery_address.address_line2, - 'houseNo': '', - }, - 'company': delivery_address.address_title, - 'person': { - 'title': delivery_contact.title, - 'firstname': delivery_contact.first_name, - 'lastname': delivery_contact.last_name - }, - 'phone': { - 'phoneNumber': delivery_contact.phone, - 'phoneNumberPrefix': delivery_contact.phone_prefix - }, - 'email': delivery_contact.email, - }, - 'shipmentDetails': { - 'contentDescription': description_of_content, - 'shipmentType': 'PARCEL', - 'shipmentSettings': { - 'saturdayDelivery': False, - 'ddp': False, - 'insurance': False, - 'pickupOrder': False, - 'pickupTailLift': False, - 'deliveryTailLift': False, - 'holidayDelivery': False, - }, - 'goodsValue': value_of_goods, - 'parcelList': parcel_list, - 'pickupInterval': { - 'date': pickup_date - } - } - } - - if service_info: - payload['service'] = { - 'baseServiceDetails': { - 'id': service_info['id'], - 'name': service_info['service_name'], - 'carrier': service_info['carrier'], - 'priceInfo': service_info['price_info'], - }, - 'supportedExWorkType': [], - 'messages': [''], - 'description': '', - 'serviceInfo': '', - } - payload['shipmentNotification'] = { - 'trackingNotification': { - 'deliveryNotification': True, - 'problemNotification': True, - 'emails': [tracking_notific_email], - 'notificationText': '', - }, - 'recipientNotification': { - 'notificationText': '', - 'emails': [ shipment_notific_email ] - } - } - payload['labelEmail'] = True - return payload - -def get_letmeship_label(shipment_id): - try: - # Retrieve shipment label from LetMeShip - api_id = frappe.db.get_single_value('LetMeShip','api_id') - api_password = frappe.db.get_single_value('LetMeShip','api_password') - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Origin': 'string' - } - url = 'https://api.letmeship.com/v1/shipments/{id}/documents?types=LABEL'\ - .format(id=shipment_id) - shipment_label_response = requests.get( - url, - auth=(api_id,api_password), - headers=headers - ) - shipment_label_response_data = json.loads(shipment_label_response.text) - if 'documents' in shipment_label_response_data: - for label in shipment_label_response_data['documents']: - if 'data' in label: - return json.dumps(label['data']) - else: - frappe.throw( - _('Error occurred while printing Shipment: {0}') - .format(shipment_label_response_data['message']) - ) - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred while printing Shipment: {0}') - .format(str(exc)), - indicator='orange', - alert=True - ) - - -def get_letmeship_tracking_data(shipment_id): - # return letmeship tracking data - api_id = frappe.db.get_single_value('LetMeShip','api_id') - api_password = frappe.db.get_single_value('LetMeShip','api_password') - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Origin': 'string' - } - try: - url = 'https://api.letmeship.com/v1/tracking?shipmentid={id}'.format(id=shipment_id) - tracking_data_response = requests.get( - url, - auth=(api_id, api_password), - headers=headers - ) - tracking_data = json.loads(tracking_data_response.text) - if 'awbNumber' in tracking_data: - tracking_status = 'In Progress' - if tracking_data['lmsTrackingStatus'].startswith('DELIVERED'): - tracking_status = 'Delivered' - if tracking_data['lmsTrackingStatus'] == 'RETURNED': - tracking_status = 'Returned' - if tracking_data['lmsTrackingStatus'] == 'LOST': - tracking_status = 'Lost' - tracking_url = get_tracking_url( - carrier=tracking_data['carrier'], - tracking_number=tracking_data['awbNumber'] - ) - return { - 'awb_number': tracking_data['awbNumber'], - 'tracking_status': tracking_status, - 'tracking_status_info': tracking_data['lmsTrackingStatus'], - 'tracking_url': tracking_url, - } - elif 'message' in tracking_data: - frappe.throw( - _('Error occurred while updating Shipment: {0}') - .format(tracking_data['message']) - ) - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred while updating Shipment: {0}') - .format(str(exc)), - indicator='orange', - alert=True - ) - - -def get_parcel_list(shipment_parcel, description_of_content): - parcel_list = [] - for parcel in shipment_parcel: - formatted_parcel = {} - formatted_parcel['height'] = parcel.get('height') - formatted_parcel['width'] = parcel.get('width') - formatted_parcel['length'] = parcel.get('length') - formatted_parcel['weight'] = parcel.get('weight') - formatted_parcel['quantity'] = parcel.get('count') - formatted_parcel['contentDescription'] = description_of_content - parcel_list.append(formatted_parcel) - return parcel_list - -def set_letmeship_specific_fields(pickup_contact, delivery_contact): - pickup_contact.phone_prefix = pickup_contact.phone[:3] - pickup_contact.phone = re.sub('[^A-Za-z0-9]+', '', pickup_contact.phone[3:]) - - pickup_contact.title = 'MS' - if pickup_contact.gender == 'Male': - pickup_contact.title = 'MR' - - delivery_contact.phone_prefix = delivery_contact.phone[:3] - delivery_contact.phone = re.sub('[^A-Za-z0-9]+', '', delivery_contact.phone[3:]) - - delivery_contact.title = 'MS' - if delivery_contact.gender == 'Male': - delivery_contact.title = 'MR' \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py b/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py deleted file mode 100644 index 3439e4fd72..0000000000 --- a/erpnext/erpnext_integrations/doctype/letmeship/test_letmeship.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 TestLetMeShip(unittest.TestCase): - pass diff --git a/erpnext/erpnext_integrations/doctype/packlink/__init__.py b/erpnext/erpnext_integrations/doctype/packlink/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.js b/erpnext/erpnext_integrations/doctype/packlink/packlink.js deleted file mode 100644 index da864584f6..0000000000 --- a/erpnext/erpnext_integrations/doctype/packlink/packlink.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Packlink', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.json b/erpnext/erpnext_integrations/doctype/packlink/packlink.json deleted file mode 100644 index a56595e9a1..0000000000 --- a/erpnext/erpnext_integrations/doctype/packlink/packlink.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "actions": [], - "creation": "2020-07-22 10:45:17.672439", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enabled", - "api_key" - ], - "fields": [ - { - "default": "0", - "fieldname": "enabled", - "fieldtype": "Check", - "label": "Enabled" - }, - { - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key", - "read_only_depends_on": "eval:doc.enabled == 0" - } - ], - "issingle": 1, - "links": [], - "modified": "2020-08-05 16:33:59.720980", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Packlink", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/packlink/packlink.py b/erpnext/erpnext_integrations/doctype/packlink/packlink.py deleted file mode 100644 index 1db08c3149..0000000000 --- a/erpnext/erpnext_integrations/doctype/packlink/packlink.py +++ /dev/null @@ -1,240 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import json -import frappe -import requests -from frappe import _ -from frappe.model.document import Document -from erpnext.erpnext_integrations.utils import get_tracking_url - -PACKLINK_PROVIDER = 'Packlink' - -class Packlink(Document): - pass - -def get_packlink_available_services(pickup_address, delivery_address, shipment_parcel,pickup_date): - # Retrieve rates at PackLink from specification stated. - from_zip = pickup_address.pincode - from_country_code = pickup_address.country_code - to_zip = delivery_address.pincode - to_country_code = delivery_address.country_code - shipment_parcel_params = '' - parcel_list = packlink_get_parcel_list(json.loads(shipment_parcel)) - for (index, parcel) in enumerate(parcel_list): - shipment_parcel_params += 'packages[{index}][height]={height}&packages[{index}][length]={length}&packages[{index}][weight]={weight}&packages[{index}][width]={width}&'.format( - index=index, - height=parcel['height'], - length=parcel['length'], - weight=parcel['weight'], - width=parcel['width'] - ) - url = 'https://api.packlink.com/v1/services?from[country]={}&from[zip]={}&to[country]={}&to[zip]={}&{}sortBy=totalPrice&source=PRO'.format( - from_country_code, - from_zip, - to_country_code, - to_zip, - shipment_parcel_params - ) - api_key = frappe.db.get_single_value('Packlink', 'api_key') - enabled = frappe.db.get_single_value('Packlink', 'enabled') - if not api_key or not enabled: - return [] - try: - responses = requests.get(url, headers={'Authorization': api_key}) - responses_dict = json.loads(responses.text) - # If an error occured on the api. Show the error message - if 'messages' in responses_dict: - frappe.msgprint( - _('Packlink: {0}' - .format(str(responses_dict['messages'][0]['message'])) - ), - indicator='orange', - alert=True - ) - available_services = [] - for response in responses_dict: - if parse_pickup_date(pickup_date) \ - in response['available_dates'].keys(): - available_service = frappe._dict() - available_service.service_provider = PACKLINK_PROVIDER - available_service.carrier = response['carrier_name'] - available_service.carrier_name = response['name'] - available_service.service_name = '' - available_service.is_preferred = 0 - available_service.total_price = response['price']['base_price'] - available_service.actual_price = response['price']['total_price'] - available_service.service_id = response['id'] - available_service.available_dates = response['available_dates'] - available_services.append(available_service) - - return available_services - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred on Packlink: {0}') - .format(str(exc)), indicator='orange', - alert=True - ) - return [] - - -def create_packlink_shipment(pickup_address, delivery_address, shipment_parcel, - description_of_content, pickup_date, value_of_goods, pickup_contact, - delivery_contact, service_info): - # Create a transaction at PackLink - enabled = frappe.db.get_single_value('Packlink', 'enabled') - if not enabled: - frappe.throw(_('Packlink integration is not enabled')) - api_key = frappe.db.get_single_value('Packlink', 'api_key') - from_country_code = pickup_address.country_code - to_country_code = delivery_address.country_code - data = { - 'additional_data': { - 'postal_zone_id_from': '', - 'postal_zone_name_from': pickup_address.country, - 'postal_zone_id_to': '', - 'postal_zone_name_to': delivery_address.country, - }, - 'collection_date': parse_pickup_date(pickup_date), - 'collection_time': '', - 'content': description_of_content, - 'contentvalue': value_of_goods, - 'content_second_hand': False, - 'from': { - 'city': pickup_address.city, - 'company': pickup_address.address_title, - 'country': from_country_code, - 'email': pickup_contact.email, - 'name': pickup_contact.first_name, - 'phone': pickup_contact.phone, - 'state': pickup_address.country, - 'street1': pickup_address.address_line1, - 'street2': pickup_address.address_line2, - 'surname': pickup_contact.last_name, - 'zip_code': pickup_address.pincode, - }, - 'insurance': {'amount': 0, 'insurance_selected': False}, - 'price': {}, - 'packages': packlink_get_parcel_list(json.loads(shipment_parcel)), - 'service_id': service_info['service_id'], - 'to': { - 'city': delivery_address.city, - 'company': delivery_address.address_title, - 'country': to_country_code, - 'email': delivery_contact.email, - 'name': delivery_contact.first_name, - 'phone': delivery_contact.phone, - 'state': delivery_address.country, - 'street1': delivery_address.address_line1, - 'street2': delivery_address.address_line2, - 'surname': delivery_contact.last_name, - 'zip_code': delivery_address.pincode, - }, - } - - url = 'https://api.packlink.com/v1/shipments' - headers = { - 'Authorization': api_key, - 'Content-Type': 'application/json' - } - try: - response_data = requests.post(url, json=data, headers=headers) - response_data = json.loads(response_data.text) - if 'reference' in response_data: - return { - 'service_provider': PACKLINK_PROVIDER, - 'shipment_id': response_data['reference'], - 'carrier': service_info['carrier'], - 'carrier_service': service_info['service_name'], - 'shipment_amount': service_info['actual_price'], - 'awb_number': '', - } - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint( - _('Error occurred while creating Shipment: {0}') - .format(str(exc)), - indicator='orange', - alert=True - ) - - -def get_packlink_label(shipment_id): - # Retrieve shipment label from PackLink - enabled = frappe.db.get_single_value('Packlink', 'enabled') - if not enabled: - frappe.throw(_('Packlink integration is not enabled')) - api_key = frappe.db.get_single_value('Packlink', 'api_key') - headers = { - 'Authorization': api_key, - 'Content-Type': 'application/json' - } - shipment_label_response = requests.get( - 'https://api.packlink.com/v1/shipments/{id}/labels'.format(id=shipment_id), - headers=headers - ) - shipment_label = json.loads(shipment_label_response.text) - if shipment_label: - return shipment_label - else: - frappe.msgprint(_('Shipment ID not found')) - - -def get_packlink_tracking_data(shipment_id): - # Get Packlink Tracking Info - enabled = frappe.db.get_single_value('Packlink', 'enabled') - if not enabled: - frappe.throw(_('Packlink integration is not enabled')) - api_key = frappe.db.get_single_value('Packlink', 'api_key') - headers = { - 'Authorization': api_key, - 'Content-Type': 'application/json' - } - try: - url = 'https://api.packlink.com/v1/shipments/{id}'.format(id=shipment_id) - tracking_data_response = requests.get(url, headers=headers) - tracking_data = json.loads(tracking_data_response.text) - if 'trackings' in tracking_data: - tracking_status = 'In Progress' - if tracking_data['state'] == 'DELIVERED': - tracking_status = 'Delivered' - if tracking_data['state'] == 'RETURNED': - tracking_status = 'Returned' - if tracking_data['state'] == 'LOST': - tracking_status = 'Lost' - awb_number = None if not tracking_data['trackings'] else tracking_data['trackings'][0] - tracking_url = get_tracking_url( - carrier=tracking_data['carrier'], - tracking_number=awb_number - ) - return { - 'awb_number': awb_number, - 'tracking_status': tracking_status, - 'tracking_status_info': tracking_data['state'], - 'tracking_url': tracking_url - } - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( - str(exc)), indicator='orange', alert=True) - return [] - - -def packlink_get_parcel_list(shipment_parcel): - parcel_list = [] - for parcel in shipment_parcel: - for count in range(parcel.get('count')): - formatted_parcel = {} - formatted_parcel['height'] = parcel.get('height') - formatted_parcel['width'] = parcel.get('width') - formatted_parcel['length'] = parcel.get('length') - formatted_parcel['weight'] = parcel.get('weight') - parcel_list.append(formatted_parcel) - return parcel_list - - -def parse_pickup_date(pickup_date): - return pickup_date.replace('-', '/') \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/packlink/test_packlink.py b/erpnext/erpnext_integrations/doctype/packlink/test_packlink.py deleted file mode 100644 index 106ae51f7c..0000000000 --- a/erpnext/erpnext_integrations/doctype/packlink/test_packlink.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 TestPacklink(unittest.TestCase): - pass diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/__init__.py b/erpnext/erpnext_integrations/doctype/sendcloud/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js deleted file mode 100644 index 3b85236863..0000000000 --- a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('SendCloud', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json deleted file mode 100644 index 37b6898cba..0000000000 --- a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "actions": [], - "creation": "2020-08-18 09:48:50.836233", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enabled", - "api_key", - "api_secret" - ], - "fields": [ - { - "default": "0", - "fieldname": "enabled", - "fieldtype": "Check", - "label": "Enabled" - }, - { - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key", - "read_only_depends_on": "eval:doc.enabled == 0" - }, - { - "fieldname": "api_secret", - "fieldtype": "Password", - "label": "API Secret", - "read_only_depends_on": "eval:doc.enabled == 0" - } - ], - "index_web_pages_for_search": 1, - "issingle": 1, - "links": [], - "modified": "2020-10-21 10:28:57.710549", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "SendCloud", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py b/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py deleted file mode 100644 index d30af15eb5..0000000000 --- a/erpnext/erpnext_integrations/doctype/sendcloud/sendcloud.py +++ /dev/null @@ -1,168 +0,0 @@ -# -*- 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 requests -import frappe -import json -from frappe import _ -from frappe.model.document import Document - -SENDCLOUD_PROVIDER = 'SendCloud' - -class SendCloud(Document): - pass - -def get_sendcloud_available_services(delivery_address, shipment_parcel): - # Retrieve rates at SendCloud from specification stated. - api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret']) - if not enabled or not api_key or not api_secret: - return [] - - try: - url = 'https://panel.sendcloud.sc/api/v2/shipping_methods' - responses = requests.get(url, auth=(api_key, api_secret)) - responses_dict = json.loads(responses.text) - - available_services = [] - for service in responses_dict['shipping_methods']: - for country in service['countries']: - if country['iso_2'] == delivery_address.country_code: - available_service = frappe._dict() - available_service.service_provider = 'SendCloud' - available_service.carrier = service['carrier'] - available_service.service_name = service['name'] - available_service.total_price = total_parcel_price(country['price'], json.loads(shipment_parcel)) - available_service.service_id = service['id'] - available_services.append(available_service) - return available_services - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint(_('Error occurred on SendCloud: {0}').format( - str(exc)), indicator='orange', alert=True) - -def create_sendcloud_shipment( - shipment, - delivery_address, - delivery_contact, - service_info, - shipment_parcel, - description_of_content, - value_of_goods -): - # Create a transaction at SendCloud - api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret']) - if not enabled or not api_key or not api_secret: - return [] - - parcels = [] - for i, parcel in enumerate(json.loads(shipment_parcel), start=1): - parcel_data = { - 'name': "{} {}".format(delivery_contact.first_name, delivery_contact.last_name), - 'company_name': delivery_address.address_title, - 'address': delivery_address.address_line1, - 'address_2': delivery_address.address_line2 or '', - 'city': delivery_address.city, - 'postal_code': delivery_address.pincode, - 'telephone': delivery_contact.phone, - 'request_label': True, - 'email': delivery_contact.email, - 'data': [], - 'country': delivery_address.country_code, - 'shipment': { - 'id': service_info['service_id'] - }, - 'order_number': "{}-{}".format(shipment, i), - 'external_reference': "{}-{}".format(shipment, i), - 'weight': parcel.get('weight'), - 'parcel_items': get_parcel_items(parcel, description_of_content, value_of_goods) - } - parcels.append(parcel_data) - data = { - 'parcels': parcels - } - try: - url = 'https://panel.sendcloud.sc/api/v2/parcels?errors=verbose' - response_data = requests.post(url, json=data, auth=(api_key, api_secret)) - response_data = json.loads(response_data.text) - if 'failed_parcels' in response_data: - frappe.msgprint(_('Error occurred while creating Shipment: {0}' - ).format(response_data['failed_parcels'][0]['errors']), indicator='orange', - alert=True) - else: - shipment_id = ', '.join([str(x['id']) for x in response_data['parcels']]) - awb_number = ', '.join([str(x['tracking_number']) for x in response_data['parcels']]) - return { - 'service_provider': 'SendCloud', - 'shipment_id': shipment_id, - 'carrier': service_info['carrier'], - 'carrier_service': service_info['service_name'], - 'shipment_amount': service_info['total_price'], - 'awb_number': awb_number - } - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint(_('Error occurred while creating Shipment: {0}').format( - str(exc)), indicator='orange', alert=True) - -def get_sendcloud_label(shipment_id): - # Retrieve shipment label from SendCloud - api_key = frappe.db.get_single_value('SendCloud', 'api_key') - api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') - shipment_id_list = shipment_id.split(', ') - label_urls = [] - for ship_id in shipment_id_list: - shipment_label_response = \ - requests.get('https://panel.sendcloud.sc/api/v2/labels/{id}'.format(id=ship_id), auth=(api_key, api_secret)) - shipment_label = json.loads(shipment_label_response.text) - label_urls.append(shipment_label['label']['label_printer']) - if len(label_urls): - return label_urls - else: - frappe.msgprint(_('Shipment ID not found')) - -def get_sendcloud_tracking_data(shipment_id): - # return SendCloud tracking data - try: - api_key = frappe.db.get_single_value('SendCloud', 'api_key') - api_secret = frappe.db.get_single_value('SendCloud', 'api_secret') - shipment_id_list = shipment_id.split(', ') - awb_number = [] - tracking_status = [] - tracking_status_info = [] - tracking_urls = [] - for ship_id in shipment_id_list: - tracking_data_response = \ - requests.get('https://panel.sendcloud.sc/api/v2/parcels/{id}'.format(id=ship_id), auth=(api_key, api_secret)) - tracking_data = json.loads(tracking_data_response.text) - tracking_urls.append(tracking_data['parcel']['tracking_url']) - awb_number.append(tracking_data['parcel']['tracking_number']) - tracking_status.append(tracking_data['parcel']['status']['message']) - tracking_status_info.append(tracking_data['parcel']['status']['message']) - return { - 'awb_number': ', '.join(awb_number), - 'tracking_status': ', '.join(tracking_status), - 'tracking_status_info': ', '.join(tracking_status_info), - 'tracking_url': ', '.join(tracking_urls) - } - except Exception as exc: - frappe.log_error(frappe.get_traceback()) - frappe.msgprint(_('Error occurred while updating Shipment: {0}').format( - str(exc)), indicator='orange', alert=True) - -def total_parcel_price(parcel_price, shipment_parcel): - count = 0 - for parcel in shipment_parcel: - count += parcel.get('count') - return parcel_price * count - -def get_parcel_items(parcel, description_of_content, value_of_goods): - parcel_list = [] - formatted_parcel = {} - formatted_parcel['description'] = description_of_content - formatted_parcel['quantity'] = parcel.get('count') - formatted_parcel['weight'] = parcel.get('weight') - formatted_parcel['value'] = value_of_goods - parcel_list.append(formatted_parcel) - return parcel_list \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py b/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py deleted file mode 100644 index 5cbe80e8ac..0000000000 --- a/erpnext/erpnext_integrations/doctype/sendcloud/test_sendcloud.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 TestSendCloud(unittest.TestCase): - pass diff --git a/erpnext/stock/doctype/parcel_service/__init__.py b/erpnext/stock/doctype/parcel_service/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.js b/erpnext/stock/doctype/parcel_service/parcel_service.js deleted file mode 100644 index 43b8ed5bf8..0000000000 --- a/erpnext/stock/doctype/parcel_service/parcel_service.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Parcel Service', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.json b/erpnext/stock/doctype/parcel_service/parcel_service.json deleted file mode 100644 index 9960acf4ae..0000000000 --- a/erpnext/stock/doctype/parcel_service/parcel_service.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "field:parcel_service_name", - "creation": "2020-07-23 10:35:38.211715", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "parcel_service_name", - "parcel_service_code", - "url_reference" - ], - "fields": [ - { - "fieldname": "parcel_service_name", - "fieldtype": "Data", - "label": "Parcel Service Name", - "unique": 1 - }, - { - "fieldname": "parcel_service_code", - "fieldtype": "Data", - "label": "Parcel Service Code" - }, - { - "fieldname": "url_reference", - "fieldtype": "Data", - "label": "URL Reference" - } - ], - "links": [], - "modified": "2020-07-23 10:35:38.211715", - "modified_by": "Administrator", - "module": "Stock", - "name": "Parcel Service", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/stock/doctype/parcel_service/parcel_service.py b/erpnext/stock/doctype/parcel_service/parcel_service.py deleted file mode 100644 index e46ac76ef7..0000000000 --- a/erpnext/stock/doctype/parcel_service/parcel_service.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 ParcelService(Document): - pass diff --git a/erpnext/stock/doctype/parcel_service/test_parcel_service.py b/erpnext/stock/doctype/parcel_service/test_parcel_service.py deleted file mode 100644 index c2f96d9cb0..0000000000 --- a/erpnext/stock/doctype/parcel_service/test_parcel_service.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 TestParcelService(unittest.TestCase): - pass diff --git a/erpnext/stock/doctype/parcel_service_type/__init__.py b/erpnext/stock/doctype/parcel_service_type/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js deleted file mode 100644 index 31d54536c0..0000000000 --- a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Parcel Service Type Alias', { - parcel_type_alias: function(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - if (row.parcel_type_alias) { - frappe.model.set_value(cdt, cdn, 'parcel_service', frm.doc.parcel_service); - frm.refresh_field('parcel_service_type_alias'); - } - } -}); diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json deleted file mode 100644 index 3c0c4d5f80..0000000000 --- a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "autoname": "format: {parcel_service} - {parcel_service_type}", - "creation": "2020-07-23 10:47:43.794083", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "parcel_service", - "parcel_service_type", - "description", - "section_break_4", - "parcel_service_type_alias", - "column_break_6", - "section_break_7", - "show_in_preferred_services_list" - ], - "fields": [ - { - "fieldname": "parcel_service", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parcel Service", - "options": "Parcel Service", - "reqd": 1 - }, - { - "fieldname": "parcel_service_type", - "fieldtype": "Data", - "label": "Parcel Service Type", - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description" - }, - { - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "parcel_service_type_alias", - "fieldtype": "Table", - "label": "Parcel Service Type Alias", - "options": "Parcel Service Type Alias" - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "default": "0", - "fieldname": "show_in_preferred_services_list", - "fieldtype": "Check", - "label": "Show in Preferred Services List" - } - ], - "links": [], - "modified": "2020-07-23 10:47:43.794083", - "modified_by": "Administrator", - "module": "Stock", - "name": "Parcel Service Type", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py b/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py deleted file mode 100644 index b55528c359..0000000000 --- a/erpnext/stock/doctype/parcel_service_type/parcel_service_type.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- 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 ParcelServiceType(Document): - pass - -def match_parcel_service_type_alias(parcel_service_type, parcel_service): - # Match and return Parcel Service Type Alias to Parcel Service Type if exists. - if frappe.db.exists('Parcel Service', parcel_service): - matched_parcel_service_type = \ - frappe.db.get_value('Parcel Service Type Alias', { - 'parcel_type_alias': parcel_service_type, - 'parcel_service': parcel_service - }, 'parent') - if matched_parcel_service_type: - parcel_service_type = matched_parcel_service_type - return parcel_service_type diff --git a/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py b/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py deleted file mode 100644 index e214264acc..0000000000 --- a/erpnext/stock/doctype/parcel_service_type/test_parcel_service_type.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 TestParcelServiceType(unittest.TestCase): - pass diff --git a/erpnext/stock/doctype/parcel_service_type_alias/__init__.py b/erpnext/stock/doctype/parcel_service_type_alias/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json deleted file mode 100644 index 8e7731e6c1..0000000000 --- a/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "actions": [], - "creation": "2020-07-23 10:47:23.626510", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "parcel_service", - "parcel_type_alias" - ], - "fields": [ - { - "fieldname": "parcel_service", - "fieldtype": "Link", - "hidden": 1, - "in_list_view": 1, - "label": "Parcel Service", - "options": "Parcel Service", - "read_only": 1 - }, - { - "fieldname": "parcel_type_alias", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Parcel Type Alias", - "reqd": 1 - } - ], - "istable": 1, - "links": [], - "modified": "2020-07-23 10:47:23.626510", - "modified_by": "Administrator", - "module": "Stock", - "name": "Parcel Service Type Alias", - "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/parcel_service_type_alias/parcel_service_type_alias.py b/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.py deleted file mode 100644 index fd0a7d8b49..0000000000 --- a/erpnext/stock/doctype/parcel_service_type_alias/parcel_service_type_alias.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 ParcelServiceTypeAlias(Document): - pass diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index fc0b05f8af..aa792a4883 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -114,26 +114,6 @@ frappe.ui.form.on('Shipment', { }); }, refresh: function(frm) { - if (frm.doc.docstatus === 1 && !frm.doc.shipment_id) { - frm.add_custom_button(__('Fetch Shipping Rates'), function() { - return frm.events.fetch_shipping_rates(frm); - }); - } - if (frm.doc.shipment_id) { - frm.add_custom_button(__('Print Shipping Label'), function() { - return frm.events.print_shipping_label(frm); - }, __('Tools')); - if (frm.doc.tracking_status != 'Delivered') { - frm.add_custom_button(__('Update Tracking'), function() { - return frm.events.update_tracking(frm, frm.doc.service_provider, frm.doc.shipment_id); - }, __('Tools')); - - frm.add_custom_button(__('Track Status'), function() { - const urls = frm.doc.tracking_url.split(', '); - urls.forEach(url => window.open(url)); - }, __('View')); - } - } $('div[data-fieldname=pickup_address] > div > .clearfix').hide(); $('div[data-fieldname=pickup_contact] > div > .clearfix').hide(); $('div[data-fieldname=delivery_address] > div > .clearfix').hide(); @@ -598,90 +578,6 @@ frappe.ui.form.on('Shipment', { frm.refresh_fields("pickup_from_send_shipping_notification"); frm.refresh_fields("pickup_from_subscribe_to_status_updates"); } - }, - fetch_shipping_rates: function(frm) { - if (!frm.doc.shipment_id) { - frappe.call({ - method: "erpnext.stock.doctype.shipment.shipment.fetch_shipping_rates", - freeze: true, - freeze_message: __("Fetching Shipping Rates"), - args: { - pickup_from_type: frm.doc.pickup_from_type, - delivery_to_type: frm.doc.delivery_to_type, - pickup_address_name: frm.doc.pickup_address_name, - delivery_address_name: frm.doc.delivery_address_name, - shipment_parcel: frm.doc.shipment_parcel, - description_of_content: frm.doc.description_of_content, - pickup_date: frm.doc.pickup_date, - pickup_contact_name: frm.doc.pickup_from_type === 'Company' ? frm.doc.pickup_contact_person : frm.doc.pickup_contact_name, - delivery_contact_name: frm.doc.delivery_contact_name, - value_of_goods: frm.doc.value_of_goods - }, - callback: function(r) { - if (r.message) { - select_from_available_services(frm, r.message); - } - else { - frappe.throw(__("No Shipment Services available")); - } - } - }); - } - else { - frappe.throw(__("Shipment already created")); - } - }, - print_shipping_label: function(frm) { - frappe.call({ - method: "erpnext.stock.doctype.shipment.shipment.print_shipping_label", - freeze: true, - freeze_message: __("Printing Shipping Label"), - args: { - shipment_id: frm.doc.shipment_id, - service_provider: frm.doc.service_provider - }, - callback: function(r) { - if (r.message) { - if (frm.doc.service_provider == "LetMeShip") { - var array = JSON.parse(r.message); - // Uint8Array for unsigned bytes - array = new Uint8Array(array); - const file = new Blob([array], {type: "application/pdf"}); - const file_url = URL.createObjectURL(file); - window.open(file_url); - } - else { - if (Array.isArray(r.message)) { - r.message.forEach(url => window.open(url)); - } else { - window.open(r.message); - } - } - } - } - }); - }, - update_tracking: function(frm, service_provider, shipment_id) { - let delivery_notes = []; - (frm.doc.shipment_delivery_note || []).forEach((d) => { - delivery_notes.push(d.delivery_note); - }); - frappe.call({ - method: "erpnext.stock.doctype.shipment.shipment.update_tracking", - freeze: true, - freeze_message: __("Updating Tracking"), - args: { - shipment: frm.doc.name, - shipment_id: shipment_id, - service_provider: service_provider, - delivery_notes: delivery_notes - }, - callback: function(r) { - if (!r.exc) { - frm.reload_doc(); - } - } - }); } }); @@ -712,82 +608,3 @@ var validate_duplicate = function(frm, table, fieldname, index){ : frm.doc[table].some((detail, i) => detail.email === fieldname && !(index === i)) ); }; - -function select_from_available_services(frm, available_services) { - var headers = [ __("Service Provider"), __("Carrier"), __("Carrier’s Service"), __("Price"), "" ]; - cur_frm.render_available_services = function(d, headers, data){ - const arranged_data = data.reduce((prev, curr) => { - if (curr.is_preferred) { - prev.preferred_services.push(curr); - } else { - prev.other_services.push(curr); - } - return prev; - }, { preferred_services: [], other_services: [] }); - d.fields_dict.available_services.$wrapper.html( - frappe.render_template('shipment_service_selector', - {'header_columns': headers, 'data': arranged_data} - ) - ); - }; - const d = new frappe.ui.Dialog({ - title: __("Select Shipment Service to create Shipment"), - fields: [ - { - fieldtype:'HTML', - fieldname:"available_services", - label: __('Available Services') - } - ] - }); - cur_frm.render_available_services(d, headers, available_services); - let shipment_notific_email = []; - let tracking_notific_email = []; - (frm.doc.shipment_notification_subscription || []).forEach((d) => { - if (!d.unsubscribed) { - shipment_notific_email.push(d.email); - } - }); - (frm.doc.shipment_status_update_subscription || []).forEach((d) => { - if (!d.unsubscribed) { - tracking_notific_email.push(d.email); - } - }); - let delivery_notes = []; - (frm.doc.shipment_delivery_note || []).forEach((d) => { - delivery_notes.push(d.delivery_note); - }); - cur_frm.select_row = function(service_data){ - frappe.call({ - method: "erpnext.stock.doctype.shipment.shipment.create_shipment", - freeze: true, - freeze_message: __("Creating Shipment"), - args: { - shipment: frm.doc.name, - pickup_from_type: frm.doc.pickup_from_type, - delivery_to_type: frm.doc.delivery_to_type, - pickup_address_name: frm.doc.pickup_address_name, - delivery_address_name: frm.doc.delivery_address_name, - shipment_parcel: frm.doc.shipment_parcel, - description_of_content: frm.doc.description_of_content, - pickup_date: frm.doc.pickup_date, - pickup_contact_name: frm.doc.pickup_from_type === 'Company' ? frm.doc.pickup_contact_person : frm.doc.pickup_contact_name, - delivery_contact_name: frm.doc.delivery_contact_name, - value_of_goods: frm.doc.value_of_goods, - service_data: service_data, - shipment_notific_email: shipment_notific_email, - tracking_notific_email: tracking_notific_email, - delivery_notes: delivery_notes - }, - callback: function(r) { - if (!r.exc) { - frm.reload_doc(); - frappe.msgprint(__("Shipment created with {0}, ID is {1}", [r.message.service_provider, r.message.shipment_id])); - frm.events.update_tracking(frm, r.message.service_provider, r.message.shipment_id); - } - } - }); - d.hide(); - }; - d.show(); -} diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index bbfbb719be..9ac6102ded 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -358,28 +358,24 @@ { "fieldname": "service_provider", "fieldtype": "Data", - "label": "Service Provider", - "read_only": 1 + "label": "Service Provider" }, { "fieldname": "shipment_id", "fieldtype": "Data", - "label": "Shipment ID", - "read_only": 1 + "label": "Shipment ID" }, { "fieldname": "shipment_amount", "fieldtype": "Currency", "label": "Shipment Amount", - "precision": "2", - "read_only": 1 + "precision": "2" }, { "fieldname": "status", "fieldtype": "Select", "label": "Status", - "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted", - "read_only": 1 + "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted" }, { "fieldname": "tracking_url", @@ -391,27 +387,23 @@ { "fieldname": "carrier", "fieldtype": "Data", - "label": "Carrier", - "read_only": 1 + "label": "Carrier" }, { "fieldname": "carrier_service", "fieldtype": "Data", - "label": "Carrier Service", - "read_only": 1 + "label": "Carrier Service" }, { "fieldname": "awb_number", "fieldtype": "Data", - "label": "AWB Number", - "read_only": 1 + "label": "AWB Number" }, { "fieldname": "tracking_status", "fieldtype": "Select", "label": "Tracking Status", - "options": "\nIn Progress\nDelivered\nReturned\nLost", - "read_only": 1 + "options": "\nIn Progress\nDelivered\nReturned\nLost" }, { "fieldname": "tracking_status_info", @@ -468,7 +460,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-09-29 13:59:50.241744", + "modified": "2020-11-20 16:19:06.157106", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index 9b3c976ca4..4e16f95533 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -10,10 +10,6 @@ from frappe.utils import flt from frappe.model.document import Document from erpnext.accounts.party import get_party_shipping_address from frappe.contacts.doctype.contact.contact import get_default_contact -from erpnext.erpnext_integrations.doctype.letmeship.letmeship import LETMESHIP_PROVIDER, get_letmeship_available_services, create_letmeship_shipment, get_letmeship_label, get_letmeship_tracking_data -from erpnext.erpnext_integrations.doctype.packlink.packlink import PACKLINK_PROVIDER, get_packlink_available_services, create_packlink_shipment, get_packlink_label, get_packlink_tracking_data -from erpnext.erpnext_integrations.doctype.sendcloud.sendcloud import SENDCLOUD_PROVIDER, get_sendcloud_available_services, create_sendcloud_shipment, get_sendcloud_label, get_sendcloud_tracking_data -from erpnext.stock.doctype.parcel_service_type.parcel_service_type import match_parcel_service_type_alias class Shipment(Document): def validate(self): @@ -36,159 +32,6 @@ class Shipment(Document): if flt(parcel.weight) <= 0: frappe.throw(_('Parcel weight cannot be 0')) -@frappe.whitelist() -def fetch_shipping_rates(pickup_from_type, delivery_to_type, pickup_address_name, delivery_address_name, - shipment_parcel, description_of_content, pickup_date, value_of_goods, - pickup_contact_name=None, delivery_contact_name=None): - # Return Shipping Rates for the various Shipping Providers - shipment_prices = [] - letmeship_enabled = frappe.db.get_single_value('LetMeShip','enabled') - packlink_enabled = frappe.db.get_single_value('Packlink','enabled') - sendcloud_enabled = frappe.db.get_single_value('SendCloud','enabled') - pickup_address = get_address(pickup_address_name) - delivery_address = get_address(delivery_address_name) - if letmeship_enabled: - pickup_contact = None - delivery_contact = None - if pickup_from_type != 'Company': - pickup_contact = get_contact(pickup_contact_name) - else: - pickup_contact = get_company_contact(user=pickup_contact_name) - - if delivery_to_type != 'Company': - delivery_contact = get_contact(delivery_contact_name) - else: - delivery_contact = get_company_contact(user=pickup_contact_name) - letmeship_prices = get_letmeship_available_services( - delivery_to_type=delivery_to_type, - pickup_address=pickup_address, - delivery_address=delivery_address, - shipment_parcel=shipment_parcel, - description_of_content=description_of_content, - pickup_date=pickup_date, - value_of_goods=value_of_goods, - pickup_contact=pickup_contact, - delivery_contact=delivery_contact, - ) - letmeship_prices = match_parcel_service_type_carrier(letmeship_prices, ['carrier', 'carrier_name']) - shipment_prices = shipment_prices + letmeship_prices - if packlink_enabled: - packlink_prices = get_packlink_available_services( - pickup_address=pickup_address, - delivery_address=delivery_address, - shipment_parcel=shipment_parcel, - pickup_date=pickup_date - ) - packlink_prices = match_parcel_service_type_carrier(packlink_prices, ['carrier_name', 'carrier']) - shipment_prices = shipment_prices + packlink_prices - if sendcloud_enabled and pickup_from_type == 'Company': - sendcloud_prices = get_sendcloud_available_services( - delivery_address=delivery_address, - shipment_parcel=shipment_parcel - ) - shipment_prices = shipment_prices + sendcloud_prices - shipment_prices = sorted(shipment_prices, key=lambda k:k['total_price']) - return shipment_prices - -@frappe.whitelist() -def create_shipment(shipment, pickup_from_type, delivery_to_type, pickup_address_name, - delivery_address_name, shipment_parcel, description_of_content, pickup_date, - value_of_goods, service_data, shipment_notific_email, tracking_notific_email, - pickup_contact_name=None, delivery_contact_name=None, delivery_notes=[]): - # Create Shipment for the selected provider - service_info = json.loads(service_data) - shipment_info = None - pickup_contact = None - delivery_contact = None - pickup_address = get_address(pickup_address_name) - delivery_address = get_address(delivery_address_name) - if pickup_from_type != 'Company': - pickup_contact = get_contact(pickup_contact_name) - else: - pickup_contact = get_company_contact(user=pickup_contact_name) - - if delivery_to_type != 'Company': - delivery_contact = get_contact(delivery_contact_name) - else: - delivery_contact = get_company_contact(user=pickup_contact_name) - if service_info['service_provider'] == LETMESHIP_PROVIDER: - shipment_info = create_letmeship_shipment( - pickup_address=pickup_address, - delivery_address=delivery_address, - shipment_parcel=shipment_parcel, - description_of_content=description_of_content, - pickup_date=pickup_date, - value_of_goods=value_of_goods, - pickup_contact=pickup_contact, - delivery_contact=delivery_contact, - service_info=service_info, - shipment_notific_email=shipment_notific_email, - tracking_notific_email=tracking_notific_email, - ) - - if service_info['service_provider'] == PACKLINK_PROVIDER: - shipment_info = create_packlink_shipment( - pickup_address=pickup_address, - delivery_address=delivery_address, - shipment_parcel=shipment_parcel, - description_of_content=description_of_content, - pickup_date=pickup_date, - value_of_goods=value_of_goods, - pickup_contact=pickup_contact, - delivery_contact=delivery_contact, - service_info=service_info, - ) - - if service_info['service_provider'] == SENDCLOUD_PROVIDER: - shipment_info = create_sendcloud_shipment( - shipment=shipment, - delivery_address=delivery_address, - shipment_parcel=shipment_parcel, - description_of_content=description_of_content, - value_of_goods=value_of_goods, - delivery_contact=delivery_contact, - service_info=service_info, - ) - - if shipment_info: - fields = ['service_provider', 'carrier', 'carrier_service', 'shipment_id', 'shipment_amount', 'awb_number'] - for field in fields: - frappe.db.set_value('Shipment', shipment, field, shipment_info.get(field)) - frappe.db.set_value('Shipment', shipment, 'status', 'Booked') - if delivery_notes: - update_delivery_note(delivery_notes=delivery_notes, shipment_info=shipment_info) - return shipment_info - - -@frappe.whitelist() -def print_shipping_label(service_provider, shipment_id): - if service_provider == LETMESHIP_PROVIDER: - shipping_label = get_letmeship_label(shipment_id) - elif service_provider == PACKLINK_PROVIDER: - shipping_label = get_packlink_label(shipment_id) - elif service_provider == SENDCLOUD_PROVIDER: - shipping_label = get_sendcloud_label(shipment_id) - return shipping_label - - -@frappe.whitelist() -def update_tracking(shipment, service_provider, shipment_id, delivery_notes=[]): - # Update Tracking info in Shipment - tracking_data = None - if service_provider == LETMESHIP_PROVIDER: - tracking_data = get_letmeship_tracking_data(shipment_id) - elif service_provider == PACKLINK_PROVIDER: - tracking_data = get_packlink_tracking_data(shipment_id) - elif service_provider == SENDCLOUD_PROVIDER: - tracking_data = get_sendcloud_tracking_data(shipment_id) - if tracking_data: - if delivery_notes: - update_delivery_note(delivery_notes=delivery_notes, tracking_info=tracking_data) - frappe.db.set_value('Shipment', shipment, 'awb_number', tracking_data.get('awb_number')) - frappe.db.set_value('Shipment', shipment, 'tracking_status', tracking_data.get('tracking_status')) - frappe.db.set_value('Shipment', shipment, 'tracking_status_info', tracking_data.get('tracking_status_info')) - frappe.db.set_value('Shipment', shipment, 'tracking_url', tracking_data.get('tracking_url')) - @frappe.whitelist() def get_address_name(ref_doctype, docname): # Return address name @@ -199,90 +42,6 @@ def get_contact_name(ref_doctype, docname): # Return address name return get_default_contact(ref_doctype, docname) -def update_delivery_note(delivery_notes, shipment_info=None, tracking_info=None): - # Update Shipment Info in Delivery Note - # Using db_set since some services might not exist - for delivery_note in json.loads(delivery_notes): - dl_doc = frappe.get_doc('Delivery Note', delivery_note) - if shipment_info: - dl_doc.db_set('delivery_type', 'Parcel Service') - dl_doc.db_set('parcel_service', shipment_info.get('carrier')) - dl_doc.db_set('parcel_service_type', shipment_info.get('carrier_service')) - if tracking_info: - dl_doc.db_set('tracking_number', tracking_info.get('awb_number')) - dl_doc.db_set('tracking_url', tracking_info.get('tracking_url')) - dl_doc.db_set('tracking_status', tracking_info.get('tracking_status')) - dl_doc.db_set('tracking_status_info', tracking_info.get('tracking_status_info')) - - -def update_tracking_info(): - # Daily scheduled event to update Tracking info for not delivered Shipments - # Also Updates the related Delivery Notes - shipments = frappe.get_all('Shipment', filters={ - 'docstatus': 1, - 'status': 'Booked', - 'shipment_id': ['!=', ''], - 'tracking_status': ['!=', 'Delivered'], - }) - for shipment in shipments: - shipment_doc = frappe.get_doc('Shipment', shipment.name) - tracking_info = \ - update_tracking( - shipment_doc.service_provider, - shipment_doc.shipment_id, - shipment_doc.shipment_delivery_notes - ) - if tracking_info: - shipment_doc.db_set('awb_number', tracking_info.get('awb_number')) - shipment_doc.db_set('tracking_url', tracking_info.get('tracking_url')) - shipment_doc.db_set('tracking_status', tracking_info.get('tracking_status')) - shipment_doc.db_set('tracking_status_info', tracking_info.get('tracking_status_info')) - - -def get_address(address_name): - address = frappe.db.get_value('Address', address_name, [ - 'address_title', - 'address_line1', - 'address_line2', - 'city', - 'pincode', - 'country', - ], as_dict=1) - address.country_code = frappe.db.get_value('Country', address.country, 'code').upper() - if not address.pincode or address.pincode == '': - frappe.throw(_("Postal Code is mandatory to continue.
\ - Please set Postal Code for Address {1}" - ).format(address_name, address_name)) - address.pincode = address.pincode.replace(' ', '') - address.city = address.city.strip() - return address - - -def get_contact(contact_name): - contact = frappe.db.get_value('Contact', contact_name, [ - 'first_name', - 'last_name', - 'email_id', - 'phone', - 'mobile_no', - 'gender', - ], as_dict=1) - if not contact.last_name: - frappe.throw(_("Last Name is mandatory to continue.
\ - Please set Last Name for Contact {1}" - ).format(contact_name, contact_name)) - if not contact.phone: - contact.phone = contact.mobile_no - return contact - -def match_parcel_service_type_carrier(shipment_prices, reference): - for idx, prices in enumerate(shipment_prices): - service_name = match_parcel_service_type_alias(prices.get(reference[0]), prices.get(reference[1])) - is_preferred = frappe.db.get_value('Parcel Service Type', service_name, 'show_in_preferred_services_list') - shipment_prices[idx].service_name = service_name - shipment_prices[idx].is_preferred = is_preferred - return shipment_prices - @frappe.whitelist() def get_company_contact(user): contact = frappe.db.get_value('User', user, [ @@ -295,4 +54,4 @@ def get_company_contact(user): ], as_dict=1) if not contact.phone: contact.phone = contact.mobile_no - return contact \ No newline at end of file + return contact diff --git a/erpnext/stock/doctype/shipment/shipment_service_selector.html b/erpnext/stock/doctype/shipment/shipment_service_selector.html deleted file mode 100644 index 4ccbe34e9e..0000000000 --- a/erpnext/stock/doctype/shipment/shipment_service_selector.html +++ /dev/null @@ -1,74 +0,0 @@ -{% if (data.preferred_services.length || data.other_services.length) { %} -
-
{{ __("Preferred Services") }}
- {% if (data.preferred_services.length) { %} - - - - {% for (var i = 0; i < header_columns.length; i++) { %} - - {% } %} - - - - {% for (var i = 0; i < data.preferred_services.length; i++) { %} - - - - - - - - {% } %} - -
{{ header_columns[i] }}
{{ data.preferred_services[i].service_provider }}{{ data.preferred_services[i].carrier }}{{ data.preferred_services[i].service_name }}{{ format_currency(data.preferred_services[i].total_price, 'EUR', 2) }} - -
- {% } else { %} -
{{ __("No Preferred Services Available") }}
- {% } %} -
{{ __("Other Services") }}
- {% if (data.other_services.length) { %} - - - - {% for (var i = 0; i < header_columns.length; i++) { %} - - {% } %} - - - - {% for (var i = 0; i < data.other_services.length; i++) { %} - - - - - - - - {% } %} - -
{{ header_columns[i] }}
{{ data.other_services[i].service_provider }}{{ data.other_services[i].carrier }}{{ data.other_services[i].service_name }}{{ format_currency(data.other_services[i].total_price, 'EUR', 2) }} - -
- {% } else { %} -
{{ __("No Services Available") }}
- {% } %} -
-{% } else { %} -
{{ __("No Services Available") }}
-{% } %} - - \ No newline at end of file From 28055f483dff5c31c3babff2f21902c77ea5f6d7 Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Mon, 23 Nov 2020 06:12:50 +0000 Subject: [PATCH 205/283] fix(shipment): change shipment test and refactor shipment.js --- .../doctype/delivery_note/delivery_note.py | 1 + erpnext/stock/doctype/shipment/shipment.js | 101 +++--------------- erpnext/stock/doctype/shipment/shipment.json | 18 +++- .../stock/doctype/shipment/test_shipment.py | 101 ++---------------- 4 files changed, 38 insertions(+), 183 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 26e4f1633e..979e83df69 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -619,6 +619,7 @@ def make_shipment(source_name, target_doc=None): "name": "prevdoc_detail_docname", "parent": "prevdoc_docname", "parenttype": "prevdoc_doctype", + "base_amount": "grand_total" } } }, target_doc, postprocess) diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index aa792a4883..62070e4e55 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -23,76 +23,20 @@ frappe.ui.form.on('Shipment', { }, onload: function(frm) { frm.set_query("delivery_address_name", () => { - let link_doctype = ''; - let link_name = ''; - let is_your_company_address = 0; - if (frm.doc.delivery_to_type == 'Customer') { - link_doctype = 'Customer'; - link_name = frm.doc.delivery_customer; - } - if (frm.doc.delivery_to_type == 'Supplier') { - link_doctype = 'Supplier'; - link_name = frm.doc.delivery_supplier; - } - if (frm.doc.delivery_to_type == 'Company') { - link_doctype = 'Company'; - link_name = frm.doc.delivery_company; - is_your_company_address = 1; - } - return frm.events.address_query(frm, link_doctype, link_name, is_your_company_address); + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + return frm.events.address_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to], frm.doc.delivery_to_type === 'Company' ? 1 : 0); }); frm.set_query("pickup_address_name", () => { - let link_doctype = ''; - let link_name = ''; - let is_your_company_address = 0; - if (frm.doc.pickup_from_type == 'Customer') { - link_doctype = 'Customer'; - link_name = frm.doc.pickup_customer; - } - if (frm.doc.pickup_from_type == 'Supplier') { - link_doctype = 'Supplier'; - link_name = frm.doc.pickup_supplier; - } - if (frm.doc.pickup_from_type == 'Company') { - link_doctype = 'Company'; - link_name = frm.doc.pickup_company; - is_your_company_address = 1; - } - return frm.events.address_query(frm, link_doctype, link_name, is_your_company_address); + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + return frm.events.address_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from], frm.doc.pickup_from_type === 'Company' ? 1 : 0); }); frm.set_query("delivery_contact_name", () => { - let link_doctype = ''; - let link_name = ''; - if (frm.doc.delivery_to_type == 'Customer') { - link_doctype = 'Customer'; - link_name = frm.doc.delivery_customer; - } - if (frm.doc.delivery_to_type == 'Supplier') { - link_doctype = 'Supplier'; - link_name = frm.doc.delivery_supplier; - } - if (frm.doc.delivery_to_type == 'Company') { - link_doctype = 'Company'; - link_name = frm.doc.delivery_company; - } - return frm.events.contact_query(frm, link_doctype, link_name); + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + return frm.events.contact_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to]); }); frm.set_query("pickup_contact_name", () => { - let link_doctype = ''; - let link_name = ''; - if (frm.doc.pickup_from_type == 'Customer') { - link_doctype = 'Customer'; - link_name = frm.doc.pickup_customer; - } - if (frm.doc.pickup_from_type == 'Supplier') { - link_doctype = 'Supplier'; - link_name = frm.doc.pickup_supplier; - } - if (frm.doc.pickup_from_type == 'Company') { - link_doctype = 'Company'; - link_name = frm.doc.pickup_company; - } - return frm.events.contact_query(frm, link_doctype, link_name); + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + return frm.events.contact_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from]); }); frm.set_query("delivery_note", "shipment_delivery_note", function() { let customer = ''; @@ -120,24 +64,10 @@ frappe.ui.form.on('Shipment', { $('div[data-fieldname=delivery_contact] > div > .clearfix').hide(); }, before_save: function(frm) { - if (frm.doc.delivery_to_type == 'Company') { - frm.set_value("delivery_to", frm.doc.delivery_company); - } - if (frm.doc.delivery_to_type == 'Customer') { - frm.set_value("delivery_to", frm.doc.delivery_customer); - } - if (frm.doc.delivery_to_type == 'Supplier') { - frm.set_value("delivery_to", frm.doc.delivery_supplier); - } - if (frm.doc.pickup_from_type == 'Company') { - frm.set_value("pickup", frm.doc.pickup_company); - } - if (frm.doc.pickup_from_type == 'Customer') { - frm.set_value("pickup", frm.doc.pickup_customer); - } - if (frm.doc.pickup_from_type == 'Supplier') { - frm.set_value("pickup", frm.doc.pickup_supplier); - } + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + frm.set_value("delivery_to", frm.doc[delivery_to]); + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + frm.set_value("pickup", frm.doc[pickup_from]); }, set_pickup_company_address: function(frm) { frappe.db.get_value('Address', { @@ -476,18 +406,11 @@ frappe.ui.form.on('Shipment', { current_min = '00'; current_hour = Number(current_hour)+1; } - if (Number(current_hour) > 19 || Number(current_hour) === 19){ - frappe.throw(__("Today's pickup time is over, please select different date")); - } - current_hour = (current_hour < 10) ? '0' + current_hour : current_hour; let pickup_time = current_hour +':'+ current_min; return pickup_time; }, set_pickup_to_time: function(frm) { let pickup_to_hour = Number(frm.doc.pickup_from.split(':')[0])+5; - if (Number(pickup_to_hour) > 19 || Number(pickup_to_hour) === 19){ - pickup_to_hour = 19; - } let pickup_to_min = frm.doc.pickup_from.split(':')[1]; let pickup_to = pickup_to_hour +':'+ pickup_to_min; frm.set_value("pickup_to", pickup_to); diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index 9ac6102ded..7e2c5baace 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -460,13 +460,28 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-11-20 16:19:06.157106", + "modified": "2020-11-23 16:26:28.132608", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", "owner": "Administrator", "permissions": [ { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -476,6 +491,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index 6a06930e82..f61b87fd41 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -7,89 +7,19 @@ from datetime import date, timedelta import frappe import unittest -from erpnext.stock.doctype.shipment.shipment import fetch_shipping_rates -from erpnext.stock.doctype.shipment.shipment import create_shipment -from erpnext.stock.doctype.shipment.shipment import update_tracking +from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment class TestShipment(unittest.TestCase): - pass - - def test_shipment_booking(self): - shipment = create_test_shipment() - try: - shipment.submit() - except: - frappe.throw('Error occurred on submit shipment') - doc, rate, tracking_data = make_shipment_transaction(shipment) - if doc and rate and tracking_data: - self.assertEqual(doc.service_provider, rate.get('service_provider')) - self.assertEqual(doc.shipment_amount, rate.get('actual_price')) - self.assertEqual(doc.carrier, rate.get('carrier')) - self.assertEqual(doc.tracking_status, tracking_data.get('tracking_status')) - self.assertEqual(doc.tracking_url, tracking_data.get('tracking_url')) - def test_shipment_from_delivery_note(self): delivery_note = create_test_delivery_note() - try: - delivery_note.submit() - except: - frappe.throw('An error occurred.') - + delivery_note.submit() shipment = create_test_shipment([ delivery_note ]) - try: - shipment.submit() - except: - frappe.throw('Error occurred on submit shipment') - doc, rate, tracking_data = make_shipment_transaction(shipment) - if doc and rate and tracking_data: - self.assertEqual(doc.service_provider, rate.get('service_provider')) - self.assertEqual(doc.shipment_amount, rate.get('actual_price')) - self.assertEqual(doc.carrier, rate.get('carrier')) - self.assertEqual(doc.tracking_status, tracking_data.get('tracking_status')) - self.assertEqual(doc.tracking_url, tracking_data.get('tracking_url')) - - - -def make_shipment_transaction(shipment): - shipment_parcel = convert_shipmet_parcel(shipment.shipment_parcel) - shipment_rates = fetch_shipping_rates(shipment.pickup_from_type, shipment.delivery_to_type, - shipment.pickup_address_name, shipment.delivery_address_name, - shipment_parcel, shipment.description_of_content, - shipment.pickup_date, shipment.value_of_goods, - pickup_contact_name=shipment.pickup_contact_name, - delivery_contact_name=shipment.delivery_contact_name - ) - if len(shipment_rates) > 0: - # We are taking the first shipment rate - rate = shipment_rates[0] - new_shipment = create_shipment( - shipment=shipment.name, - pickup_from_type=shipment.pickup_from_type, - delivery_to_type=shipment.delivery_to_type, - pickup_address_name=shipment.pickup_address_name, - delivery_address_name=shipment.delivery_address_name, - shipment_parcel=shipment_parcel, - description_of_content=shipment.description_of_content, - pickup_date=shipment.pickup_date, - pickup_contact_name=shipment.pickup_contact_name, - delivery_contact_name=shipment.delivery_contact_name, - value_of_goods=shipment.value_of_goods, - service_data=json.dumps(rate), - shipment_notific_email=None, - tracking_notific_email=None, - delivery_notes=None - ) - service_provider = rate.get('service_provider') - shipment_id = new_shipment.get('shipment_id') - tracking_data = update_tracking( - shipment.name, - service_provider, - shipment_id, - delivery_notes=None - ) - doc = frappe.get_doc('Shipment', shipment.name) - return doc, rate, tracking_data - return None, None, None + shipment.submit() + second_shipment = make_shipment(delivery_note.name) + self.assertEqual(second_shipment.value_of_goods, delivery_note.grand_total) + self.assertEqual(second_shipment.grand_total, delivery_note.grand_total) + self.assertEqual(len(second_shipment.shipment_delivery_note), 1) + self.assertEqual(second_shipment.shipment_delivery_note[0].delivery_note, delivery_note.name) def create_test_delivery_note(): company = get_shipment_company() @@ -316,18 +246,3 @@ def create_shipment_item(item_name, company_name): except: frappe.throw('An error occurred.') return item - - -def convert_shipmet_parcel(shipmet_parcel): - data = [] - for parcel in shipmet_parcel: - data.append( - { - "length": parcel.length, - "width": parcel.width, - "height": parcel.height, - "weight": parcel.weight, - "count": parcel.count - } - ) - return json.dumps(data) From 99361b4a9eb88b56b90474fe42ccd2ba4f081afb Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Mon, 23 Nov 2020 09:03:13 +0000 Subject: [PATCH 206/283] chore: remove notification details section --- erpnext/stock/doctype/shipment/shipment.js | 72 -------------------- erpnext/stock/doctype/shipment/shipment.json | 56 +-------------- 2 files changed, 1 insertion(+), 127 deletions(-) diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index 62070e4e55..2832c8c72a 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -102,8 +102,6 @@ frappe.ui.form.on('Shipment', { frm.set_value("pickup_customer", ''); frm.set_value("pickup_company", ''); } - frm.events.remove_notific_child_table(frm, 'shipment_notification_subscription', 'Pickup'); - frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscription', 'Pickup'); }, delivery_to_type: function(frm) { if (frm.doc.delivery_to_type == 'Company') { @@ -126,8 +124,6 @@ frappe.ui.form.on('Shipment', { else { frm.toggle_display("shipment_delivery_note", true); } - frm.events.remove_notific_child_table(frm, 'shipment_notification_subscription', 'Delivery'); - frm.events.remove_notific_child_table(frm, 'shipment_status_update_subscription', 'Delivery'); }, delivery_address_name: function(frm) { if (frm.doc.delivery_to_type == 'Company') { @@ -427,80 +423,12 @@ frappe.ui.form.on('Shipment', { frm.set_value(field, ''); } }, - pickup_from_send_shipping_notification: function(frm, cdt, cdn) { - if (frm.doc.pickup_contact_email && frm.doc.pickup_from_send_shipping_notification - && !validate_duplicate(frm, 'shipment_notification_subscription', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscription", "shipment_notification_subscription"); - row.email = frm.doc.pickup_contact_email; - frm.refresh_fields("shipment_notification_subscription"); - } - if (!frm.doc.pickup_from_send_shipping_notification) { - frm.events.remove_email_row(frm, 'shipment_notification_subscription', frm.doc.pickup_contact_email); - frm.refresh_fields("shipment_notification_subscription"); - } - }, - pickup_from_subscribe_to_status_updates: function(frm, cdt, cdn) { - if (frm.doc.pickup_contact_email && frm.doc.pickup_from_subscribe_to_status_updates - && !validate_duplicate(frm, 'shipment_status_update_subscription', frm.doc.pickup_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscription", "shipment_status_update_subscription"); - row.email = frm.doc.pickup_contact_email; - frm.refresh_fields("shipment_status_update_subscription"); - } - if (!frm.doc.pickup_from_subscribe_to_status_updates) { - frm.events.remove_email_row(frm, 'shipment_status_update_subscription', frm.doc.pickup_contact_email); - frm.refresh_fields("shipment_status_update_subscription"); - } - }, - delivery_to_send_shipping_notification: function(frm, cdt, cdn) { - if (frm.doc.delivery_contact_email && frm.doc.delivery_to_send_shipping_notification - && !validate_duplicate(frm, 'shipment_notification_subscription', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)){ - let row = frappe.model.add_child(frm.doc, "Shipment Notification Subscription", "shipment_notification_subscription"); - row.email = frm.doc.delivery_contact_email; - frm.refresh_fields("shipment_notification_subscription"); - } - if (!frm.doc.delivery_to_send_shipping_notification) { - frm.events.remove_email_row(frm, 'shipment_notification_subscription', frm.doc.delivery_contact_email); - frm.refresh_fields("shipment_notification_subscription"); - } - }, - delivery_to_subscribe_to_status_updates: function(frm, cdt, cdn) { - if (frm.doc.delivery_contact_email && frm.doc.delivery_to_subscribe_to_status_updates - && !validate_duplicate(frm, 'shipment_status_update_subscription', frm.doc.delivery_contact_email, locals[cdt][cdn].idx)) { - let row = frappe.model.add_child(frm.doc, "Shipment Status Update Subscription", "shipment_status_update_subscription"); - row.email = frm.doc.delivery_contact_email; - frm.refresh_fields("shipment_status_update_subscription"); - } - if (!frm.doc.delivery_to_subscribe_to_status_updates) { - frm.events.remove_email_row(frm, 'shipment_status_update_subscription', frm.doc.delivery_contact_email); - frm.refresh_fields("shipment_status_update_subscription"); - } - }, remove_email_row: function(frm, table, fieldname) { $.each(frm.doc[table] || [], function(i, detail) { if(detail.email === fieldname){ cur_frm.get_field(table).grid.grid_rows[i].remove(); } }); - }, - remove_notific_child_table: function(frm, table, delivery_type) { - $.each(frm.doc[table] || [], function(i, detail) { - if (detail.email != frm.doc.pickup_email || detail.email != frm.doc.delivery_email){ - cur_frm.get_field(table).grid.grid_rows[i].remove(); - } - }); - frm.refresh_fields(table); - if (delivery_type == 'Delivery') { - frm.set_value("delivery_to_send_shipping_notification", 0); - frm.set_value("delivery_to_subscribe_to_status_updates", 0); - frm.refresh_fields("delivery_to_send_shipping_notification"); - frm.refresh_fields("delivery_to_subscribe_to_status_updates"); - } - else { - frm.set_value("pickup_from_send_shipping_notification", 0); - frm.set_value("pickup_from_subscribe_to_status_updates", 0); - frm.refresh_fields("pickup_from_send_shipping_notification"); - frm.refresh_fields("pickup_from_subscribe_to_status_updates"); - } } }); diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index 7e2c5baace..1ae7862bc9 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -30,14 +30,6 @@ "delivery_contact_name", "delivery_contact_email", "delivery_contact", - "notification_details_section", - "pickup_from_send_shipping_notification", - "pickup_from_subscribe_to_status_updates", - "shipment_notification_subscription", - "column_break_27", - "delivery_to_send_shipping_notification", - "delivery_to_subscribe_to_status_updates", - "shipment_status_update_subscription", "parcels_section", "shipment_parcel", "parcel_template", @@ -224,40 +216,6 @@ "fieldtype": "Small Text", "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "notification_details_section", - "fieldtype": "Section Break", - "label": "Notification Details" - }, - { - "default": "0", - "fieldname": "pickup_from_send_shipping_notification", - "fieldtype": "Check", - "label": "Send shipping notification" - }, - { - "default": "0", - "fieldname": "pickup_from_subscribe_to_status_updates", - "fieldtype": "Check", - "label": "Subscribe to status updates" - }, - { - "fieldname": "column_break_27", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "delivery_to_send_shipping_notification", - "fieldtype": "Check", - "label": "Send shipping notification" - }, - { - "default": "0", - "fieldname": "delivery_to_subscribe_to_status_updates", - "fieldtype": "Check", - "label": "Subscribe to status updates" - }, { "fieldname": "parcels_section", "fieldtype": "Section Break", @@ -437,18 +395,6 @@ "label": "Shipment Delivery Note", "options": "Shipment Delivery Note" }, - { - "fieldname": "shipment_notification_subscription", - "fieldtype": "Table", - "label": "Shipment Notification Subscription", - "options": "Shipment Notification Subscription" - }, - { - "fieldname": "shipment_status_update_subscription", - "fieldtype": "Table", - "label": "Shipment Status Update Subscription", - "options": "Shipment Status Update Subscription" - }, { "depends_on": "eval:doc.pickup_from_type === 'Company'", "fieldname": "pickup_contact_person", @@ -460,7 +406,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-11-23 16:26:28.132608", + "modified": "2020-11-23 17:00:51.600965", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", From b4b542d1c392b865c96a5b82aa3927fb95aca60d Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Mon, 23 Nov 2020 09:26:39 +0000 Subject: [PATCH 207/283] chore: linter issues and sider checks --- erpnext/stock/doctype/shipment/shipment.js | 92 ++++++++----------- erpnext/stock/doctype/shipment/shipment.py | 1 - .../stock/doctype/shipment/shipment_list.js | 2 +- .../stock/doctype/shipment/test_shipment.py | 11 +-- 4 files changed, 42 insertions(+), 64 deletions(-) diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index 2832c8c72a..5ccb7d2ff6 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -23,19 +23,19 @@ frappe.ui.form.on('Shipment', { }, onload: function(frm) { frm.set_query("delivery_address_name", () => { - let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; return frm.events.address_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to], frm.doc.delivery_to_type === 'Company' ? 1 : 0); }); frm.set_query("pickup_address_name", () => { - let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; return frm.events.address_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from], frm.doc.pickup_from_type === 'Company' ? 1 : 0); }); frm.set_query("delivery_contact_name", () => { - let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; return frm.events.contact_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to]); }); frm.set_query("pickup_contact_name", () => { - let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; return frm.events.contact_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from]); }); frm.set_query("delivery_note", "shipment_delivery_note", function() { @@ -57,16 +57,16 @@ frappe.ui.form.on('Shipment', { } }); }, - refresh: function(frm) { + refresh: function() { $('div[data-fieldname=pickup_address] > div > .clearfix').hide(); $('div[data-fieldname=pickup_contact] > div > .clearfix').hide(); $('div[data-fieldname=delivery_address] > div > .clearfix').hide(); $('div[data-fieldname=delivery_contact] > div > .clearfix').hide(); }, before_save: function(frm) { - let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}` + let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; frm.set_value("delivery_to", frm.doc[delivery_to]); - let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}` + let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; frm.set_value("pickup", frm.doc[pickup_from]); }, set_pickup_company_address: function(frm) { @@ -90,8 +90,7 @@ frappe.ui.form.on('Shipment', { frm.set_value("pickup_company", frappe.defaults.get_default('company')); frm.set_value("pickup_customer", ''); frm.set_value("pickup_supplier", ''); - } - else { + } else { frm.trigger('clear_pickup_fields'); } if (frm.doc.pickup_from_type == 'Customer') { @@ -108,8 +107,7 @@ frappe.ui.form.on('Shipment', { frm.set_value("delivery_company", frappe.defaults.get_default('company')); frm.set_value("delivery_customer", ''); frm.set_value("delivery_supplier", ''); - } - else { + } else { frm.trigger('clear_delivery_fields'); } if (frm.doc.delivery_to_type == 'Customer') { @@ -120,24 +118,21 @@ frappe.ui.form.on('Shipment', { frm.set_value("delivery_customer", ''); frm.set_value("delivery_company", ''); frm.toggle_display("shipment_delivery_note", false); - } - else { + } else { frm.toggle_display("shipment_delivery_note", true); } }, delivery_address_name: function(frm) { if (frm.doc.delivery_to_type == 'Company') { erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', true); - } - else { + } else { erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', false); } }, pickup_address_name: function(frm) { if (frm.doc.pickup_from_type == 'Company') { erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', true); - } - else { + } else { erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', false); } }, @@ -146,18 +141,16 @@ frappe.ui.form.on('Shipment', { method: "frappe.contacts.doctype.contact.contact.get_contact_details", args: { contact: contact_name }, callback: function(r) { - if(r.message) { + if (r.message) { if (!(r.message.contact_email && (r.message.contact_phone || r.message.contact_mobile))) { if (contact_type == 'Delivery') { frm.set_value('delivery_contact_name', ''); frm.set_value('delivery_contact', ''); - } - else { + } else { frm.set_value('pickup_contact_name', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__(`Email or Phone/Mobile of the Contact are mandatory to continue.
- Please set Email/Phone for the contact ${contact_name}`)); + frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") + "
" + __("Please set Email/Phone for the contact") + ` ${contact_name}`); } let contact_display = r.message.contact_display; if (r.message.contact_email) { @@ -169,13 +162,12 @@ frappe.ui.form.on('Shipment', { if (r.message.contact_mobile && !r.message.contact_phone) { contact_display += '
' + r.message.contact_mobile; } - if (contact_type == 'Delivery'){ + if (contact_type == 'Delivery') { frm.set_value('delivery_contact', contact_display); if (r.message.contact_email) { frm.set_value('delivery_contact_email', r.message.contact_email); } - } - else { + } else { frm.set_value('pickup_contact', contact_display); if (r.message.contact_email) { frm.set_value('pickup_contact_email', r.message.contact_email); @@ -246,13 +238,11 @@ frappe.ui.form.on('Shipment', { if (delivery_type == 'Delivery') { frm.set_value('delivery_company', ''); frm.set_value('delivery_contact', ''); - } - else { + } else { frm.set_value('pickup_company', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__(`Last Name, Email or Phone/Mobile of the user are mandatory to continue.
- Please first set Last Name, Email and Phone for the user ${frappe.session.user}`)); + frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
" + __("Please first set Last Name, Email and Phone for the user") + ` ${frappe.session.user}`); } let contact_display = r.full_name; if (r.email) { @@ -269,8 +259,7 @@ frappe.ui.form.on('Shipment', { if (r.email) { frm.set_value('delivery_contact_email', r.email); } - } - else { + } else { frm.set_value('pickup_contact', contact_display); if (r.email) { frm.set_value('pickup_contact_email', r.email); @@ -294,27 +283,27 @@ frappe.ui.form.on('Shipment', { delivery_customer: function(frm) { frm.trigger('clear_delivery_fields'); if (frm.doc.delivery_customer) { - frm.events.set_address_name(frm,'Customer',frm.doc.delivery_customer, 'Delivery'); - frm.events.set_contact_name(frm,'Customer',frm.doc.delivery_customer, 'Delivery'); + frm.events.set_address_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery'); + frm.events.set_contact_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery'); } }, delivery_supplier: function(frm) { frm.trigger('clear_delivery_fields'); if (frm.doc.delivery_supplier) { - frm.events.set_address_name(frm,'Supplier',frm.doc.delivery_supplier, 'Delivery'); - frm.events.set_contact_name(frm,'Supplier',frm.doc.delivery_supplier, 'Delivery'); + frm.events.set_address_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery'); + frm.events.set_contact_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery'); } }, pickup_customer: function(frm) { if (frm.doc.pickup_customer) { - frm.events.set_address_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); - frm.events.set_contact_name(frm,'Customer',frm.doc.pickup_customer, 'Pickup'); + frm.events.set_address_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup'); + frm.events.set_contact_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup'); } }, pickup_supplier: function(frm) { if (frm.doc.pickup_supplier) { - frm.events.set_address_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); - frm.events.set_contact_name(frm,'Supplier',frm.doc.pickup_supplier, 'Pickup'); + frm.events.set_address_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup'); + frm.events.set_contact_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup'); } }, set_address_name: function(frm, ref_doctype, ref_docname, delivery_type) { @@ -325,11 +314,10 @@ frappe.ui.form.on('Shipment', { docname: ref_docname }, callback: function(r) { - if(r.message) { + if (r.message) { if (delivery_type == 'Delivery') { frm.set_value('delivery_address_name', r.message); - } - else { + } else { frm.set_value('pickup_address_name', r.message); } } @@ -344,11 +332,10 @@ frappe.ui.form.on('Shipment', { docname: ref_docname }, callback: function(r) { - if(r.message) { + if (r.message) { if (delivery_type == 'Delivery') { frm.set_value('delivery_contact_name', r.message); - } - else { + } else { frm.set_value('pickup_contact_name', r.message); } } @@ -397,8 +384,7 @@ frappe.ui.form.on('Shipment', { let current_min = new Date().toLocaleString('en-US', {minute: 'numeric'}); if (current_min < 30) { current_min = '30'; - } - else { + } else { current_min = '00'; current_hour = Number(current_hour)+1; } @@ -413,19 +399,19 @@ frappe.ui.form.on('Shipment', { }, clear_pickup_fields: function(frm) { let fields = ["pickup_address_name", "pickup_contact_name", "pickup_address", "pickup_contact", "pickup_contact_email", "pickup_contact_person"]; - for (let field of fields){ + for (let field of fields) { frm.set_value(field, ''); } }, clear_delivery_fields: function(frm) { let fields = ["delivery_address_name", "delivery_contact_name", "delivery_address", "delivery_contact", "delivery_contact_email"]; - for (let field of fields){ + for (let field of fields) { frm.set_value(field, ''); } }, remove_email_row: function(frm, table, fieldname) { $.each(frm.doc[table] || [], function(i, detail) { - if(detail.email === fieldname){ + if (detail.email === fieldname) { cur_frm.get_field(table).grid.grid_rows[i].remove(); } }); @@ -437,8 +423,8 @@ frappe.ui.form.on('Shipment Delivery Note', { let row = locals[cdt][cdn]; if (row.delivery_note) { let row_index = row.idx - 1; - if(validate_duplicate(frm, 'shipment_delivery_note', row.delivery_note, row_index)) { - frappe.throw(__(`You have entered a duplicate Delivery Note on Row ${row.idx}. Please rectify and try again.`)); + if (validate_duplicate(frm, 'shipment_delivery_note', row.delivery_note, row_index)) { + frappe.throw(__("You have entered a duplicate Delivery Note on Row") + ` ${row.idx}. ` + __("Please rectify and try again.")); } } }, @@ -452,7 +438,7 @@ frappe.ui.form.on('Shipment Delivery Note', { }, }); -var validate_duplicate = function(frm, table, fieldname, index){ +var validate_duplicate = function(frm, table, fieldname, index) { return ( table === 'shipment_delivery_note' ? frm.doc[table].some((detail, i) => detail.delivery_note === fieldname && !(index === i)) diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index 4e16f95533..508af39cd5 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import frappe -import json from frappe import _ from frappe.utils import flt from frappe.model.document import Document diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js index 57e92099cb..52b052c81f 100644 --- a/erpnext/stock/doctype/shipment/shipment_list.js +++ b/erpnext/stock/doctype/shipment/shipment_list.js @@ -1,7 +1,7 @@ frappe.listview_settings['Shipment'] = { add_fields: ["status"], get_indicator: function(doc) { - if(doc.status=='Booked') { + if (doc.status=='Booked') { return [__("Booked"), "green"]; } } diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index f61b87fd41..e238e878db 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals -import json from datetime import date, timedelta import frappe @@ -222,10 +221,7 @@ def create_material_receipt(item, company): } ) stock.insert() - try: - stock.submit() - except: - frappe.throw('An error occurred.') + stock.submit() def create_shipment_item(item_name, company_name): @@ -241,8 +237,5 @@ def create_shipment_item(item_name, company_name): "default_warehouse": 'Stores - SC' } ) - try: - item.insert() - except: - frappe.throw('An error occurred.') + item.insert() return item From 8e68f128c17e5f14096edf6fbc0a12f951d63393 Mon Sep 17 00:00:00 2001 From: jbienesdev Date: Wed, 2 Dec 2020 07:54:25 +0000 Subject: [PATCH 208/283] fix: travis build error - Removed shipment notification and subscription files - Minor changes on shipment field configuration - Add shipment to desk --- erpnext/stock/desk_page/stock/stock.json | 4 +- erpnext/stock/doctype/shipment/shipment.json | 42 +++++++++++++++---- erpnext/stock/doctype/shipment/shipment.py | 7 ++++ .../stock/doctype/shipment/test_shipment.py | 7 ++-- .../shipment_delivery_note.json | 3 +- .../__init__.py | 0 .../shipment_notification_subscription.json | 40 ------------------ .../shipment_notification_subscription.py | 10 ----- .../__init__.py | 0 .../shipment_status_update_subscription.json | 40 ------------------ .../shipment_status_update_subscription.py | 10 ----- 11 files changed, 46 insertions(+), 117 deletions(-) delete mode 100644 erpnext/stock/doctype/shipment_notification_subscription/__init__.py delete mode 100644 erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json delete mode 100644 erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py delete mode 100644 erpnext/stock/doctype/shipment_status_update_subscription/__init__.py delete mode 100644 erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json delete mode 100644 erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 390fcd91e3..9068e338c3 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -8,7 +8,7 @@ { "hidden": 0, "label": "Stock Transactions", - "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]" + "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\": \"Shipment\",\n \"name\": \"Shipment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-10-07 18:40:17.130207", + "modified": "2020-12-02 15:47:41.532942", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index 1ae7862bc9..37a9cc6c02 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -234,6 +234,7 @@ "options": "Shipment Parcel Template" }, { + "depends_on": "eval:doc.docstatus !== 1\n", "fieldname": "add_template", "fieldtype": "Button", "label": "Add Template" @@ -262,6 +263,7 @@ "reqd": 1 }, { + "allow_on_submit": 1, "fieldname": "pickup_date", "fieldtype": "Date", "in_list_view": 1, @@ -269,12 +271,14 @@ "reqd": 1 }, { + "allow_on_submit": 1, "default": "09:00", "fieldname": "pickup_from", "fieldtype": "Time", "label": "Pickup from" }, { + "allow_on_submit": 1, "default": "17:00", "fieldname": "pickup_to", "fieldtype": "Time", @@ -316,57 +320,77 @@ { "fieldname": "service_provider", "fieldtype": "Data", - "label": "Service Provider" + "label": "Service Provider", + "no_copy": 1, + "print_hide": 1 }, { "fieldname": "shipment_id", "fieldtype": "Data", - "label": "Shipment ID" + "label": "Shipment ID", + "no_copy": 1, + "print_hide": 1 }, { "fieldname": "shipment_amount", "fieldtype": "Currency", "label": "Shipment Amount", - "precision": "2" + "no_copy": 1, + "precision": "2", + "print_hide": 1 }, { "fieldname": "status", "fieldtype": "Select", "label": "Status", - "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted" + "no_copy": 1, + "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted", + "print_hide": 1 }, { "fieldname": "tracking_url", "fieldtype": "Small Text", "hidden": 1, "label": "Tracking URL", + "no_copy": 1, + "print_hide": 1, "read_only": 1 }, { "fieldname": "carrier", "fieldtype": "Data", - "label": "Carrier" + "label": "Carrier", + "no_copy": 1, + "print_hide": 1 }, { "fieldname": "carrier_service", "fieldtype": "Data", - "label": "Carrier Service" + "label": "Carrier Service", + "no_copy": 1, + "print_hide": 1 }, { "fieldname": "awb_number", "fieldtype": "Data", - "label": "AWB Number" + "label": "AWB Number", + "no_copy": 1, + "print_hide": 1 }, { "fieldname": "tracking_status", "fieldtype": "Select", "label": "Tracking Status", - "options": "\nIn Progress\nDelivered\nReturned\nLost" + "no_copy": 1, + "options": "\nIn Progress\nDelivered\nReturned\nLost", + "print_hide": 1 }, { "fieldname": "tracking_status_info", "fieldtype": "Data", "label": "Tracking Status Info", + "no_copy": 1, + "print_hide": 1, "read_only": 1 }, { @@ -406,7 +430,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-11-23 17:00:51.600965", + "modified": "2020-12-02 15:43:44.607039", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index 508af39cd5..de0c243b05 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -13,6 +13,7 @@ from frappe.contacts.doctype.contact.contact import get_default_contact class Shipment(Document): def validate(self): self.validate_weight() + self.set_value_of_goods() if self.docstatus == 0: self.status = 'Draft' @@ -31,6 +32,12 @@ class Shipment(Document): if flt(parcel.weight) <= 0: frappe.throw(_('Parcel weight cannot be 0')) + def set_value_of_goods(self): + value_of_goods = 0 + for entry in self.get("shipment_delivery_note"): + value_of_goods += flt(entry.get("grand_total")) + self.value_of_goods = value_of_goods if value_of_goods else self.value_of_goods + @frappe.whitelist() def get_address_name(ref_doctype, docname): # Return address name diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index e238e878db..e1fa207a21 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -16,7 +16,6 @@ class TestShipment(unittest.TestCase): shipment.submit() second_shipment = make_shipment(delivery_note.name) self.assertEqual(second_shipment.value_of_goods, delivery_note.grand_total) - self.assertEqual(second_shipment.grand_total, delivery_note.grand_total) self.assertEqual(len(second_shipment.shipment_delivery_note), 1) self.assertEqual(second_shipment.shipment_delivery_note[0].delivery_note, delivery_note.name) @@ -49,7 +48,7 @@ def create_test_delivery_note(): return delivery_note -def create_test_shipment(delivery_notes=[]): +def create_test_shipment(delivery_notes = None): company = get_shipment_company() company_address = get_shipment_company_address(company.name) customer = get_shipment_customer() @@ -74,7 +73,7 @@ def create_test_shipment(delivery_notes=[]): shipment.pickup_to = '17:00' shipment.description_of_content = 'unit test entry' for delivery_note in delivery_notes: - shipment.append('shipment_delivery_notes', + shipment.append('shipment_delivery_note', { "delivery_note": delivery_note.name } @@ -229,7 +228,7 @@ def create_shipment_item(item_name, company_name): item.item_name = item_name item.item_code = item_name item.item_group = 'All Item Groups' - item.opening_stock = 'Nos' + item.stock_uom = 'Nos' item.standard_rate = 50 item.append('item_defaults', { diff --git a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json index 9651e3f945..8625913718 100644 --- a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json +++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json @@ -18,7 +18,6 @@ "reqd": 1 }, { - "fetch_from": "delivery_note.grand_total", "fieldname": "grand_total", "fieldtype": "Currency", "in_list_view": 1, @@ -28,7 +27,7 @@ ], "istable": 1, "links": [], - "modified": "2020-07-09 12:55:01.134270", + "modified": "2020-12-02 15:44:34.028703", "modified_by": "Administrator", "module": "Stock", "name": "Shipment Delivery Note", diff --git a/erpnext/stock/doctype/shipment_notification_subscription/__init__.py b/erpnext/stock/doctype/shipment_notification_subscription/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json deleted file mode 100644 index d927d9902e..0000000000 --- a/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "actions": [], - "creation": "2020-07-09 12:49:09.185552", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "email", - "unsubscribed" - ], - "fields": [ - { - "fieldname": "email", - "fieldtype": "Data", - "in_list_view": 1, - "label": "email", - "reqd": 1, - "unique": 1 - }, - { - "default": "0", - "fieldname": "unsubscribed", - "fieldtype": "Check", - "in_list_view": 1, - "label": "unsubscribed" - } - ], - "istable": 1, - "links": [], - "modified": "2020-07-09 12:55:14.217387", - "modified_by": "Administrator", - "module": "Stock", - "name": "Shipment Notification Subscription", - "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/shipment_notification_subscription/shipment_notification_subscription.py b/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py deleted file mode 100644 index c816e4343c..0000000000 --- a/erpnext/stock/doctype/shipment_notification_subscription/shipment_notification_subscription.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 ShipmentNotificationSubscription(Document): - pass diff --git a/erpnext/stock/doctype/shipment_status_update_subscription/__init__.py b/erpnext/stock/doctype/shipment_status_update_subscription/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json deleted file mode 100644 index a7fe4a4a0a..0000000000 --- a/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "actions": [], - "creation": "2020-07-09 12:51:10.656612", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "email", - "unsubscribed" - ], - "fields": [ - { - "fieldname": "email", - "fieldtype": "Data", - "in_list_view": 1, - "label": "email", - "reqd": 1, - "unique": 1 - }, - { - "default": "0", - "fieldname": "unsubscribed", - "fieldtype": "Check", - "in_list_view": 1, - "label": "unsubscribed" - } - ], - "istable": 1, - "links": [], - "modified": "2020-07-09 12:55:27.615463", - "modified_by": "Administrator", - "module": "Stock", - "name": "Shipment Status Update Subscription", - "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/shipment_status_update_subscription/shipment_status_update_subscription.py b/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py deleted file mode 100644 index 1b006d7efc..0000000000 --- a/erpnext/stock/doctype/shipment_status_update_subscription/shipment_status_update_subscription.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 ShipmentStatusUpdateSubscription(Document): - pass From 523c464a92bf5e17850b55da2d9980e7b00ea274 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 2 Dec 2020 16:01:35 +0530 Subject: [PATCH 209/283] fix: Test Payment Based on Leave Application (Travis) --- erpnext/payroll/doctype/salary_slip/test_salary_slip.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 71cb4083ed..5daf1d439d 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -118,11 +118,6 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 4) - #Gross pay calculation based on attendances - gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) - - self.assertEqual(flt(ss.gross_pay, 2), flt(gross_pay, 2)) - frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") def test_salary_slip_with_holidays_included(self): From 9abc685504cc14a949fe922d343ff42d5f0f9a2a Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 2 Dec 2020 21:13:50 +0530 Subject: [PATCH 210/283] feat: add jinja templating in contract template --- erpnext/crm/doctype/contract/contract.js | 32 +++++++++++-------- erpnext/crm/doctype/contract/contract.json | 4 ++- .../contract_template/contract_template.py | 23 +++++++++++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index ee9e895130..6c0d739c89 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -1,23 +1,27 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch("contract_template", "contract_terms", "contract_terms"); -cur_frm.add_fetch("contract_template", "requires_fulfilment", "requires_fulfilment"); - -// Add fulfilment terms from contract template into contract frappe.ui.form.on("Contract", { contract_template: function (frm) { - // Populate the fulfilment terms table from a contract template, if any if (frm.doc.contract_template) { - frappe.model.with_doc("Contract Template", frm.doc.contract_template, function () { - var tabletransfer = frappe.model.get_doc("Contract Template", frm.doc.contract_template); - - frm.doc.fulfilment_terms = []; - $.each(tabletransfer.fulfilment_terms, function (index, row) { - var d = frm.add_child("fulfilment_terms"); - d.requirement = row.requirement; - frm.refresh_field("fulfilment_terms"); - }); + frappe.call({ + method: 'erpnext.crm.doctype.contract_template.contract_template.get_contract_template', + args: { + template_name: frm.doc.contract_template, + doc: frm.doc + }, + callback: function(r) { + if(r && r.message){ + frm.set_value("contract_terms", r.message.contract_terms); + + // Populate the fulfilment terms table from a contract template, if any + r.message.contract_template.fulfilment_terms.forEach(element => { + let d = frm.add_child("fulfilment_terms"); + d.requirement = element.requirement; + }); + frm.refresh_field("fulfilment_terms"); + } + } }); } } diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json index 0026e4a02e..fbc9f1c8d3 100755 --- a/erpnext/crm/doctype/contract/contract.json +++ b/erpnext/crm/doctype/contract/contract.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "creation": "2018-04-12 06:32:04.582486", @@ -175,6 +176,7 @@ }, { "default": "0", + "fetch_from": "contract_template.requires_fulfilment", "fieldname": "requires_fulfilment", "fieldtype": "Check", "label": "Requires Fulfilment" @@ -247,7 +249,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-30 06:56:07.257932", + "modified": "2020-12-02 21:12:44.118155", "modified_by": "Administrator", "module": "CRM", "name": "Contract", diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py index 601ee9a28b..48ab8aad47 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.py +++ b/erpnext/crm/doctype/contract_template/contract_template.py @@ -5,6 +5,29 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.utils.jinja import validate_template +from six import string_types +import json class ContractTemplate(Document): pass + + def validate(self): + if self.contract_terms: + validate_template(self.contract_terms) + +@frappe.whitelist() +def get_contract_template(template_name, doc): + if isinstance(doc, string_types): + doc = json.loads(doc) + + contract_template = frappe.get_doc("Contract Template", template_name) + contract_terms = None + + if contract_template.contract_terms: + contract_terms = frappe.render_template(contract_template.contract_terms, doc) + + return { + 'contract_template': contract_template, + 'contract_terms': contract_terms + } \ No newline at end of file From 131e46bab79c6f275fd0358ffbb0510a2bc2eef4 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 2 Dec 2020 21:37:55 +0530 Subject: [PATCH 211/283] chore: add help section for Jinja --- .../contract_template/contract_template.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index 5e4582f8d3..9fc24798cd 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -11,7 +11,9 @@ "contract_terms", "sb_fulfilment", "requires_fulfilment", - "fulfilment_terms" + "fulfilment_terms", + "section_break_6", + "contract_template_help" ], "fields": [ { @@ -41,10 +43,20 @@ "fieldtype": "Table", "label": "Fulfilment Terms and Conditions", "options": "Contract Template Fulfilment Terms" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "contract_template_help", + "fieldtype": "HTML", + "label": "Contract Template Help", + "options": "

Contract Template Example

\n\n
Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" } ], "links": [], - "modified": "2020-11-11 17:49:44.879363", + "modified": "2020-12-02 21:36:53.097074", "modified_by": "Administrator", "module": "CRM", "name": "Contract Template", From 8474476316095bc94286dd8dd8f6eb2f4decf651 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 24 Nov 2020 19:04:03 +0530 Subject: [PATCH 212/283] fix: incorrect balance value in stock balance report --- erpnext/stock/report/stock_balance/stock_balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 1339d9b682..ccd01001bb 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -164,7 +164,7 @@ def get_stock_ledger_entries(filters, items): select sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate, sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, - sle.item_code as name, sle.voucher_no + sle.item_code as name, sle.voucher_no, sle.stock_value from `tabStock Ledger Entry` sle force index (posting_sort_index) where sle.docstatus < 2 %s %s @@ -197,7 +197,7 @@ def get_item_warehouse_map(filters, sle): else: qty_diff = flt(d.actual_qty) - value_diff = flt(d.stock_value_difference) + value_diff = flt(d.stock_value) - flt(qty_dict.bal_val) if d.posting_date < from_date: qty_dict.opening_qty += qty_diff From 69c5d49b25002dffd8248df3f3916a9a3d1763ab Mon Sep 17 00:00:00 2001 From: Anuja P Date: Thu, 3 Dec 2020 15:14:54 +0530 Subject: [PATCH 213/283] refactor: to avoid using SQL query to get fiscal year filters --- .../accounts/doctype/fiscal_year/fiscal_year.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index d80bc7fad1..8faf24d375 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -116,12 +116,8 @@ def auto_create_fiscal_year(): pass def get_from_and_to_date(fiscal_year): - from_and_to_date_tuple = frappe.db.sql("""select year_start_date, year_end_date - from `tabFiscal Year` where name=%s""", (fiscal_year))[0] - - from_and_to_date = { - "from_date": from_and_to_date_tuple[0], - "to_date": from_and_to_date_tuple[1] - } - - return from_and_to_date + fields = [ + "year_start_date as from_date", + "year_end_date as to_date" + ] + return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1) From 2895645f86d27aa0d80564f9ac06b53fef966e62 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 3 Dec 2020 15:39:53 +0530 Subject: [PATCH 214/283] feat: rename desk page to workspace --- erpnext/accounts/desk_page/accounting/accounting.json | 2 +- erpnext/agriculture/desk_page/agriculture/agriculture.json | 2 +- erpnext/assets/desk_page/assets/assets.json | 2 +- erpnext/buying/desk_page/buying/buying.json | 2 +- erpnext/crm/desk_page/crm/crm.json | 2 +- erpnext/education/desk_page/education/education.json | 2 +- .../desk_page/erpnext_integrations/erpnext_integrations.json | 2 +- .../erpnext_integrations_settings.json | 2 +- erpnext/healthcare/desk_page/healthcare/healthcare.json | 2 +- erpnext/hr/desk_page/hr/hr.json | 2 +- erpnext/loan_management/desk_page/loan/loan.json | 2 +- .../manufacturing/desk_page/manufacturing/manufacturing.json | 2 +- erpnext/non_profit/desk_page/non_profit/non_profit.json | 2 +- erpnext/payroll/desk_page/payroll/payroll.json | 2 +- erpnext/projects/desk_page/projects/projects.json | 2 +- erpnext/quality_management/desk_page/quality/quality.json | 2 +- erpnext/selling/desk_page/retail/retail.json | 2 +- erpnext/selling/desk_page/selling/selling.json | 2 +- erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json | 2 +- erpnext/setup/desk_page/home/home.json | 2 +- erpnext/stock/desk_page/stock/stock.json | 2 +- erpnext/support/desk_page/support/support.json | 2 +- erpnext/utilities/desk_page/utilities/utilities.json | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 0add27786b..8d24ca8291 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "accounting", diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/desk_page/agriculture/agriculture.json index 26829cbb1b..2cc252491d 100644 --- a/erpnext/agriculture/desk_page/agriculture/agriculture.json +++ b/erpnext/agriculture/desk_page/agriculture/agriculture.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "agriculture", diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 58bdc68ef2..c401581758 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "assets", diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 8e00f7fd9a..6c9c0f3011 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -12,7 +12,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "buying", diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 724ff77b05..b4fb7d8abe 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -9,7 +9,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "crm", diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index 04db5a4366..bf7496146d 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "education", diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json index a36ca8ddb7..4a5e54edd2 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends": "Integrations", "extends_another_page": 1, "hide_custom": 1, diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json index d5e6189243..d258d57131 100644 --- a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends": "Settings", "extends_another_page": 1, "hide_custom": 0, diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index cf1f6246cb..b93dda2e87 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -11,7 +11,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "healthcare", diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 218699314c..7f1af845f8 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "hr", diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index 6ef1737e1a..80d8a8049e 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "loan", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index e856a987e3..a355203e4d 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -9,7 +9,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "organization", diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/desk_page/non_profit/non_profit.json index be92d60610..da2a514810 100644 --- a/erpnext/non_profit/desk_page/non_profit/non_profit.json +++ b/erpnext/non_profit/desk_page/non_profit/non_profit.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "non-profit", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index 137fad2779..814973063d 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "money-coins-1", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 5c8ab993f3..dbbd7e1458 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "project", diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index aaee9ab857..e5fef43550 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "quality", diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json index e9beef5f91..e20f8347c2 100644 --- a/erpnext/selling/desk_page/retail/retail.json +++ b/erpnext/selling/desk_page/retail/retail.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "retail", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 0bbf17fcae..879034a0df 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -11,7 +11,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 1, "icon": "sell", diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json index a2224515e5..014f4095c1 100644 --- a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends": "Settings", "extends_another_page": 1, "hide_custom": 0, diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/desk_page/home/home.json index fa164d3a03..c041bb32fb 100644 --- a/erpnext/setup/desk_page/home/home.json +++ b/erpnext/setup/desk_page/home/home.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "getting-started", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index fbf8326088..3221dc4365 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -10,7 +10,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "stock", diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 20e60c062b..01a8676f05 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "icon": "support", diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/desk_page/utilities/utilities.json index df17303924..2f9250ee45 100644 --- a/erpnext/utilities/desk_page/utilities/utilities.json +++ b/erpnext/utilities/desk_page/utilities/utilities.json @@ -5,7 +5,7 @@ "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, - "doctype": "Desk Page", + "doctype": "Workspace", "extends_another_page": 0, "hide_custom": 0, "idx": 0, From 2fa3cac81496cc611f9f227e985022aa3127e495 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 3 Dec 2020 15:48:57 +0530 Subject: [PATCH 215/283] feat: update workspace folders --- .../accounts/{desk_page => workspace}/accounting/accounting.json | 0 .../{desk_page => workspace}/agriculture/agriculture.json | 0 erpnext/assets/{desk_page => workspace}/assets/assets.json | 0 erpnext/buying/{desk_page => workspace}/buying/buying.json | 0 erpnext/crm/{desk_page => workspace}/crm/crm.json | 0 .../education/{desk_page => workspace}/education/education.json | 0 .../erpnext_integrations/erpnext_integrations.json | 0 .../erpnext_integrations_settings.json | 0 .../{desk_page => workspace}/healthcare/healthcare.json | 0 erpnext/hr/{desk_page => workspace}/hr/hr.json | 0 erpnext/loan_management/{desk_page => workspace}/loan/loan.json | 0 .../{desk_page => workspace}/manufacturing/manufacturing.json | 0 .../{desk_page => workspace}/non_profit/non_profit.json | 0 erpnext/payroll/{desk_page => workspace}/payroll/payroll.json | 0 erpnext/projects/{desk_page => workspace}/projects/projects.json | 0 .../{desk_page => workspace}/quality/quality.json | 0 erpnext/selling/{desk_page => workspace}/retail/retail.json | 0 erpnext/selling/{desk_page => workspace}/selling/selling.json | 0 .../erpnext_settings/erpnext_settings.json | 0 erpnext/setup/{desk_page => workspace}/home/home.json | 0 erpnext/stock/{desk_page => workspace}/stock/stock.json | 0 erpnext/support/{desk_page => workspace}/support/support.json | 0 .../utilities/{desk_page => workspace}/utilities/utilities.json | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename erpnext/accounts/{desk_page => workspace}/accounting/accounting.json (100%) rename erpnext/agriculture/{desk_page => workspace}/agriculture/agriculture.json (100%) rename erpnext/assets/{desk_page => workspace}/assets/assets.json (100%) rename erpnext/buying/{desk_page => workspace}/buying/buying.json (100%) rename erpnext/crm/{desk_page => workspace}/crm/crm.json (100%) rename erpnext/education/{desk_page => workspace}/education/education.json (100%) rename erpnext/erpnext_integrations/{desk_page => workspace}/erpnext_integrations/erpnext_integrations.json (100%) rename erpnext/erpnext_integrations/{desk_page => workspace}/erpnext_integrations_settings/erpnext_integrations_settings.json (100%) rename erpnext/healthcare/{desk_page => workspace}/healthcare/healthcare.json (100%) rename erpnext/hr/{desk_page => workspace}/hr/hr.json (100%) rename erpnext/loan_management/{desk_page => workspace}/loan/loan.json (100%) rename erpnext/manufacturing/{desk_page => workspace}/manufacturing/manufacturing.json (100%) rename erpnext/non_profit/{desk_page => workspace}/non_profit/non_profit.json (100%) rename erpnext/payroll/{desk_page => workspace}/payroll/payroll.json (100%) rename erpnext/projects/{desk_page => workspace}/projects/projects.json (100%) rename erpnext/quality_management/{desk_page => workspace}/quality/quality.json (100%) rename erpnext/selling/{desk_page => workspace}/retail/retail.json (100%) rename erpnext/selling/{desk_page => workspace}/selling/selling.json (100%) rename erpnext/setup/{desk_page => workspace}/erpnext_settings/erpnext_settings.json (100%) rename erpnext/setup/{desk_page => workspace}/home/home.json (100%) rename erpnext/stock/{desk_page => workspace}/stock/stock.json (100%) rename erpnext/support/{desk_page => workspace}/support/support.json (100%) rename erpnext/utilities/{desk_page => workspace}/utilities/utilities.json (100%) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json similarity index 100% rename from erpnext/accounts/desk_page/accounting/accounting.json rename to erpnext/accounts/workspace/accounting/accounting.json diff --git a/erpnext/agriculture/desk_page/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json similarity index 100% rename from erpnext/agriculture/desk_page/agriculture/agriculture.json rename to erpnext/agriculture/workspace/agriculture/agriculture.json diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/workspace/assets/assets.json similarity index 100% rename from erpnext/assets/desk_page/assets/assets.json rename to erpnext/assets/workspace/assets/assets.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/workspace/buying/buying.json similarity index 100% rename from erpnext/buying/desk_page/buying/buying.json rename to erpnext/buying/workspace/buying/buying.json diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/workspace/crm/crm.json similarity index 100% rename from erpnext/crm/desk_page/crm/crm.json rename to erpnext/crm/workspace/crm/crm.json diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/workspace/education/education.json similarity index 100% rename from erpnext/education/desk_page/education/education.json rename to erpnext/education/workspace/education/education.json diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json similarity index 100% rename from erpnext/erpnext_integrations/desk_page/erpnext_integrations/erpnext_integrations.json rename to erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json diff --git a/erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json similarity index 100% rename from erpnext/erpnext_integrations/desk_page/erpnext_integrations_settings/erpnext_integrations_settings.json rename to erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/workspace/healthcare/healthcare.json similarity index 100% rename from erpnext/healthcare/desk_page/healthcare/healthcare.json rename to erpnext/healthcare/workspace/healthcare/healthcare.json diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/workspace/hr/hr.json similarity index 100% rename from erpnext/hr/desk_page/hr/hr.json rename to erpnext/hr/workspace/hr/hr.json diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/workspace/loan/loan.json similarity index 100% rename from erpnext/loan_management/desk_page/loan/loan.json rename to erpnext/loan_management/workspace/loan/loan.json diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json similarity index 100% rename from erpnext/manufacturing/desk_page/manufacturing/manufacturing.json rename to erpnext/manufacturing/workspace/manufacturing/manufacturing.json diff --git a/erpnext/non_profit/desk_page/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json similarity index 100% rename from erpnext/non_profit/desk_page/non_profit/non_profit.json rename to erpnext/non_profit/workspace/non_profit/non_profit.json diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json similarity index 100% rename from erpnext/payroll/desk_page/payroll/payroll.json rename to erpnext/payroll/workspace/payroll/payroll.json diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/workspace/projects/projects.json similarity index 100% rename from erpnext/projects/desk_page/projects/projects.json rename to erpnext/projects/workspace/projects/projects.json diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/workspace/quality/quality.json similarity index 100% rename from erpnext/quality_management/desk_page/quality/quality.json rename to erpnext/quality_management/workspace/quality/quality.json diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/workspace/retail/retail.json similarity index 100% rename from erpnext/selling/desk_page/retail/retail.json rename to erpnext/selling/workspace/retail/retail.json diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/workspace/selling/selling.json similarity index 100% rename from erpnext/selling/desk_page/selling/selling.json rename to erpnext/selling/workspace/selling/selling.json diff --git a/erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json similarity index 100% rename from erpnext/setup/desk_page/erpnext_settings/erpnext_settings.json rename to erpnext/setup/workspace/erpnext_settings/erpnext_settings.json diff --git a/erpnext/setup/desk_page/home/home.json b/erpnext/setup/workspace/home/home.json similarity index 100% rename from erpnext/setup/desk_page/home/home.json rename to erpnext/setup/workspace/home/home.json diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/workspace/stock/stock.json similarity index 100% rename from erpnext/stock/desk_page/stock/stock.json rename to erpnext/stock/workspace/stock/stock.json diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/workspace/support/support.json similarity index 100% rename from erpnext/support/desk_page/support/support.json rename to erpnext/support/workspace/support/support.json diff --git a/erpnext/utilities/desk_page/utilities/utilities.json b/erpnext/utilities/workspace/utilities/utilities.json similarity index 100% rename from erpnext/utilities/desk_page/utilities/utilities.json rename to erpnext/utilities/workspace/utilities/utilities.json From 26cc662a7879ad1efdcd1d12e2a6c3ea27a93916 Mon Sep 17 00:00:00 2001 From: Anuja P Date: Thu, 3 Dec 2020 17:45:34 +0530 Subject: [PATCH 216/283] feat: added filter for customer field --- erpnext/support/doctype/issue/issue.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index fe01d4b983..33191bedde 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -1,6 +1,13 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; + frm.set_query('customer', function () { + return { + filters: { + "disabled": 0 + } + } + }); frappe.db.get_value("Support Settings", {name: "Support Settings"}, ["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => { @@ -145,6 +152,7 @@ frappe.ui.form.on("Issue", { reset_sla.show(); }, + timeline_refresh: function(frm) { // create button for "Help Article" if(frappe.model.can_create('Help Article')) { From 25e058833a7dfec25812d4fa5828d94ef61713a5 Mon Sep 17 00:00:00 2001 From: Anuja P Date: Thu, 3 Dec 2020 18:13:24 +0530 Subject: [PATCH 217/283] fix: sider changes --- erpnext/support/doctype/issue/issue.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 33191bedde..c20ad5c2fd 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -6,8 +6,8 @@ frappe.ui.form.on("Issue", { filters: { "disabled": 0 } - } - }); + }; + }); frappe.db.get_value("Support Settings", {name: "Support Settings"}, ["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => { From a6a37b7c8174dd05ca710c45a1e69d25468f0baf Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 3 Dec 2020 19:24:07 +0530 Subject: [PATCH 218/283] fix: scroll elements --- erpnext/public/scss/point-of-sale.scss | 38 +++++++++---------- .../page/point_of_sale/pos_item_cart.js | 8 +--- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index f9757384cb..3e7d5dae82 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -75,22 +75,14 @@ background-color: var(--gray-50); } - .sticky-element { - position: sticky; - top: -1px; - z-index: 1; - } - > .items-selector { @extend .pos-card; grid-column: span 6 / span 6; display: flex; flex-direction: column; - overflow-y: scroll; - overflow-x: hidden; + overflow: hidden; > .filter-section { - @extend .sticky-element; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); background-color: var(--fg-color); @@ -124,6 +116,14 @@ gap: var(--margin-lg); padding: var(--padding-lg); padding-top: var(--padding-xs); + overflow-y: scroll; + overflow-x: hidden; + + &:after { + content: ""; + display: block; + height: 1px; + } > .item-wrapper { @extend .pointer-no-select; @@ -185,6 +185,7 @@ display: flex; flex-direction: column; padding: var(--padding-md) var(--padding-lg); + overflow: hidden; > .customer-field { display: flex; @@ -193,7 +194,6 @@ } > .customer-details { - @extend .sticky-element; display: flex; flex-direction: column; background-color: var(--fg-color); @@ -284,6 +284,11 @@ > .customer-transactions { height: 100%; + overflow-x: hidden; + overflow-y: scroll; + margin-right: -12px; + padding-right: 12px; + margin-left: -10px; > .no-transactions-placeholder { height: 100%; @@ -899,11 +904,9 @@ grid-column: span 4 / span 4; display: none; flex-direction: column; - overflow-y: scroll; - overflow-x: hidden; + overflow: hidden; > .filter-section { - @extend .sticky-element; display: flex; flex-direction: column; background-color: var(--fg-color); @@ -927,17 +930,17 @@ > .invoices-container { padding: var(--padding-lg); padding-top: 0px; + overflow-x: hidden; + overflow-y: scroll; } } > .past-order-summary { - // @extend .pos-card; display: none; grid-column: span 6 / span 6; flex-direction: column; align-items: center; justify-content: center; - // padding: var(--padding-lg); > .no-summary-placeholder { display: flex; @@ -1029,14 +1032,12 @@ align-items: center; justify-content: space-between; padding: var(--padding-sm) var(--padding-md); - // border-bottom: 1px solid var(--gray-300); } > .taxes-wrapper { display: flex; flex-direction: column; padding: 0px var(--padding-md); - // border-bottom: 1px solid var(--gray-300); > .tax-row { display: flex; @@ -1050,7 +1051,6 @@ display: flex; align-items: center; padding: var(--padding-sm) var(--padding-md); - // border-bottom: 1px solid var(--gray-300); > .item-name { @extend .nowrap; @@ -1081,9 +1081,7 @@ } > .grand-total { - // font-size: var(--text-lg); font-weight: 700; - // padding: var(--padding-md); } > .payments { diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index efe716de6c..3124e91325 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -791,9 +791,7 @@ erpnext.PointOfSale.ItemCart = class { this.$cart_container.css('display', 'none'); this.$customer_section.css({ 'height': '100%', - 'padding-top': '0px', - 'overflow-x': 'hidden', - 'overflow-y': 'scroll' + 'padding-top': '0px' }); this.$customer_section.find('.customer-details').html( `
@@ -829,9 +827,7 @@ erpnext.PointOfSale.ItemCart = class { this.$cart_container.css('display', 'flex'); this.$customer_section.css({ 'height': '', - 'padding-top': '', - 'overflow-x': '', - 'overflow-y': '' + 'padding-top': '' }); this.update_customer_section(); From f4d645f87a7b751c3d28df172f1d09177b665cde Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 3 Dec 2020 19:30:12 +0530 Subject: [PATCH 219/283] perf: async pos app start --- .../page/point_of_sale/pos_controller.js | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 155d35dd8f..74d212227a 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -118,28 +118,11 @@ erpnext.PointOfSale.Controller = class { this.make_app(); } - set_opening_entry_status() { - this.page.set_title_sub( - ` - - Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")} - - `); - } - make_app() { - return frappe.run_serially([ - () => frappe.dom.freeze(), - () => { - this.set_opening_entry_status(); - this.prepare_dom(); - this.prepare_components(); - this.prepare_menu(); - }, - () => this.make_new_invoice(), - () => frappe.dom.unfreeze(), - () => this.page.set_title(__('Point of Sale')), - ]); + this.prepare_dom(); + this.prepare_components(); + this.prepare_menu(); + this.make_new_invoice(); } prepare_dom() { @@ -416,8 +399,6 @@ erpnext.PointOfSale.Controller = class { }) } - - toggle_recent_order_list(show) { this.toggle_components(!show); this.recent_order_list.toggle_component(show); @@ -434,10 +415,12 @@ erpnext.PointOfSale.Controller = class { make_new_invoice() { return frappe.run_serially([ + () => frappe.dom.freeze(), () => this.make_sales_invoice_frm(), () => this.set_pos_profile_data(), () => this.set_pos_profile_status(), () => this.cart.load_invoice(), + () => frappe.dom.unfreeze() ]); } @@ -494,16 +477,6 @@ erpnext.PointOfSale.Controller = class { return this.frm.trigger("set_pos_data"); } - raise_exception_for_pos_profile() { - setTimeout(() => frappe.set_route('List', 'POS Profile'), 2000); - frappe.throw(__("POS Profile is required to use Point-of-Sale")); - } - - set_invoice_status() { - const [status, indicator] = frappe.listview_settings["POS Invoice"].get_indicator(this.frm.doc); - this.page.set_indicator(status, indicator); - } - set_pos_profile_status() { this.page.set_indicator(this.pos_profile, "blue"); } From 0139109de2ae31b2283712ee5ea555ed62afae77 Mon Sep 17 00:00:00 2001 From: Anuja P Date: Fri, 4 Dec 2020 11:28:13 +0530 Subject: [PATCH 220/283] fix: consistency check --- erpnext/support/doctype/issue/issue.js | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index c20ad5c2fd..521e671b0d 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -1,7 +1,7 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; - frm.set_query('customer', function () { + frm.set_query("customer", function () { return { filters: { "disabled": 0 @@ -28,14 +28,14 @@ frappe.ui.form.on("Issue", { }, callback: function (r) { if (r && r.message) { - frm.set_query('priority', function() { + frm.set_query("priority", function() { return { filters: { "name": ["in", r.message.priority], } }; }); - frm.set_query('service_level_agreement', function() { + frm.set_query("service_level_agreement", function() { return { filters: { "name": ["in", r.message.service_level_agreements], @@ -52,9 +52,9 @@ frappe.ui.form.on("Issue", { if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") { if (frm.doc.service_level_agreement) { frappe.call({ - 'method': 'frappe.client.get', + "method": "frappe.client.get", args: { - doctype: 'Service Level Agreement', + doctype: "Service Level Agreement", name: frm.doc.service_level_agreement }, callback: function(data) { @@ -134,8 +134,8 @@ frappe.ui.form.on("Issue", { reset_sla.clear(); frappe.show_alert({ - indicator: 'green', - message: __('Resetting Service Level Agreement.') + indicator: "green", + message: __("Resetting Service Level Agreement.") }); frm.call("reset_service_level_agreement", { @@ -155,33 +155,33 @@ frappe.ui.form.on("Issue", { timeline_refresh: function(frm) { // create button for "Help Article" - if(frappe.model.can_create('Help Article')) { + if(frappe.model.can_create("Help Article")) { // Removing Help Article button if exists to avoid multiple occurance frm.timeline.wrapper.find('.comment-header .asset-details .btn-add-to-kb').remove(); $('') .appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])')) - .on('click', function() { - var content = $(this).parents('.timeline-item:first').find('.timeline-item-content').html(); - var doc = frappe.model.get_new_doc('Help Article'); + .on("click", function() { + var content = $(this).parents(".timeline-item:first").find(".timeline-item-content").html(); + var doc = frappe.model.get_new_doc("Help Article"); doc.title = frm.doc.subject; doc.content = content; - frappe.set_route('Form', 'Help Article', doc.name); + frappe.set_route("Form", "Help Article", doc.name); }); } - if (!frm.timeline.wrapper.find('.btn-split-issue').length) { + if (!frm.timeline.wrapper.find(".btn-split-issue").length) { let split_issue = __("Split Issue") $(``) .appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])')) if (!frm.timeline.wrapper.data("split-issue-event-attached")){ - frm.timeline.wrapper.on('click', '.btn-split-issue', (e) => { + frm.timeline.wrapper.on("click", ".btn-split-issue", (e) => { var dialog = new frappe.ui.Dialog({ title: __("Split Issue"), fields: [ - {fieldname: 'subject', fieldtype: 'Data', reqd:1, label: __('Subject'), description: __('All communications including and above this shall be moved into the new Issue')} + {fieldname: "subject", fieldtype: "Data", reqd:1, label: __("Subject"), description: __("All communications including and above this shall be moved into the new Issue")} ], primary_action_label: __("Split"), primary_action: function() { @@ -234,7 +234,7 @@ function set_time_to_resolve_and_response(frm) { function get_time_left(timestamp, agreement_status) { const diff = moment(timestamp).diff(moment()); const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed"; - let indicator = (diff_display == 'Failed' && agreement_status != "Fulfilled") ? "red" : "green"; + let indicator = (diff_display == "Failed" && agreement_status != "Fulfilled") ? "red" : "green"; return {"diff_display": diff_display, "indicator": indicator}; } From 28e86cf18312155ed020065b10152f77e8747d8f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 4 Dec 2020 00:47:46 +0530 Subject: [PATCH 221/283] fix: pricing rule with transaction not working for additional product --- .../doctype/pricing_rule/pricing_rule.json | 4 ++- .../doctype/pricing_rule/test_pricing_rule.py | 31 +++++++++++++++---- .../accounts/doctype/pricing_rule/utils.py | 11 ++++++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index cc8ed4bc49..d08a854142 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -406,6 +406,7 @@ "fieldtype": "Column Break" }, { + "default": "0", "depends_on": "eval:doc.rate_or_discount==\"Rate\"", "fieldname": "rate", "fieldtype": "Currency", @@ -469,6 +470,7 @@ "options": "UOM" }, { + "description": "If rate is zero them item will be treated as \"Free Item\"", "fieldname": "free_item_rate", "fieldtype": "Currency", "label": "Rate" @@ -563,7 +565,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2020-10-28 16:53:14.416172", + "modified": "2020-12-04 00:36:24.698219", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index ec0a485bfc..af8d21d9ce 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -521,6 +521,22 @@ class TestPricingRule(unittest.TestCase): frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete() item.delete() + def test_pricing_rule_for_transaction(self): + make_item("Water Flask 1") + frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule') + make_pricing_rule(selling=1, min_qty=5, price_or_product_discount="Product", + apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10) + + si = create_sales_invoice(qty=5, do_not_submit=True) + self.assertEquals(len(si.items), 2) + self.assertEquals(si.items[1].rate, 10) + + si1 = create_sales_invoice(qty=2, do_not_submit=True) + self.assertEquals(len(si1.items), 1) + + for doc in [si, si1]: + doc.delete() + def make_pricing_rule(**args): args = frappe._dict(args) @@ -539,20 +555,23 @@ def make_pricing_rule(**args): "rate_or_discount": args.rate_or_discount or "Discount Percentage", "discount_percentage": args.discount_percentage or 0.0, "rate": args.rate or 0.0, - "margin_type": args.margin_type, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "condition": args.condition or '', "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 }) - if args.get("priority"): - doc.priority = args.get("priority") + for field in ["free_item", "free_qty", "free_item_rate", "priority", + "margin_type", "price_or_product_discount"]: + if args.get(field): + doc.set(field, args.get(field)) apply_on = doc.apply_on.replace(' ', '_').lower() child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'} - doc.append(child_table.get(doc.apply_on), { - apply_on: args.get(apply_on) or "_Test Item" - }) + + if doc.apply_on != "Transaction": + doc.append(child_table.get(doc.apply_on), { + apply_on: args.get(apply_on) or "_Test Item" + }) doc.insert(ignore_permissions=True) if args.get(apply_on) and apply_on != "item_code": diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b003328cc4..2c7cd14451 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -457,6 +457,9 @@ def apply_pricing_rule_on_transaction(doc): pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty, doc.total, pricing_rules) + if not pricing_rules: + remove_free_item(doc) + for d in pricing_rules: if d.price_or_product_discount == 'Price': if d.apply_discount_on: @@ -480,6 +483,12 @@ def apply_pricing_rule_on_transaction(doc): get_product_discount_rule(d, item_details, doc=doc) apply_pricing_rule_for_free_items(doc, item_details.free_item_data) doc.set_missing_values() + doc.calculate_taxes_and_totals() + +def remove_free_item(doc): + for d in doc.items: + if d.is_free_item: + doc.remove(d) def get_applied_pricing_rules(pricing_rules): if pricing_rules: @@ -492,7 +501,7 @@ def get_applied_pricing_rules(pricing_rules): def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): free_item = pricing_rule.free_item - if pricing_rule.same_item: + if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction': free_item = item_details.item_code or args.item_code if not free_item: From edee530d4cba4d6395e5a6f680fa5c33d962b175 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 4 Dec 2020 12:13:26 +0530 Subject: [PATCH 222/283] fix: paid amount in Sales Invoice POS return resets to 0 --- erpnext/controllers/taxes_and_totals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 81d07c1327..ad58f137ee 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -641,7 +641,8 @@ class calculate_taxes_and_totals(object): if default_mode_of_payment: self.doc.append('payments', { 'mode_of_payment': default_mode_of_payment.mode_of_payment, - 'amount': total_amount_to_pay + 'amount': total_amount_to_pay, + 'default': 1 }) else: self.doc.is_pos = 0 From 6a2431586ec82290ffad6c24d30cd4e5f777a7a4 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 4 Dec 2020 13:31:36 +0530 Subject: [PATCH 223/283] fix: Make new Customers for account missing test and set company --- .../test_opening_invoice_creation_tool.py | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py index 329d84bdb7..bdfe532b9f 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py @@ -11,15 +11,20 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_ from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account class TestOpeningInvoiceCreationTool(unittest.TestCase): - def make_invoices(self, invoice_type="Sales", company=None): + def setUp(self): + if not frappe.db.exists("Company", "_Test Opening Invoice Company"): + make_company() + + def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None): doc = frappe.get_single("Opening Invoice Creation Tool") - args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company) + args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company, + party_1=party_1, party_2=party_2) doc.update(args) return doc.make_invoices() def test_opening_sales_invoice_creation(self): property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check") - invoices = self.make_invoices() + invoices = self.make_invoices(company="_Test Opening Invoice Company") self.assertEqual(len(invoices), 2) expected_value = { @@ -45,7 +50,7 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase): self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx]) def test_opening_purchase_invoice_creation(self): - invoices = self.make_invoices(invoice_type="Purchase") + invoices = self.make_invoices(invoice_type="Purchase", company="_Test Opening Invoice Company") self.assertEqual(len(invoices), 2) expected_value = { @@ -56,9 +61,11 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase): self.check_expected_values(invoices, expected_value, "Purchase") def test_opening_sales_invoice_creation_with_missing_debit_account(self): - company = make_company() - old_default_receivable_account = frappe.db.get_value("Company", company.name, "default_receivable_account") - frappe.db.set_value("Company", company.name, "default_receivable_account", "") + company = "_Test Opening Invoice Company" + party_1, party_2 = make_customer("Customer A"), make_customer("Customer B") + + old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account") + frappe.db.set_value("Company", company, "default_receivable_account", "") if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"): cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company", @@ -68,18 +75,16 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase): "company": "_Test Opening Invoice Company", "parent_cost_center": cc.name}) cc2.insert() - frappe.db.set_value("Company", company.name, "cost_center", "Main - _TOIC") + frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC") - self.make_invoices(company="_Test Opening Invoice Company") + self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2) # Check if missing debit account error raised error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]}) self.assertTrue(error_log) # teardown - frappe.db.set_value("Company", company.name, "default_receivable_account", old_default_receivable_account) - company.delete() - frappe.get_doc("Error Log", error_log).delete() + frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account) def get_opening_invoice_creation_dict(**args): party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier" @@ -92,7 +97,7 @@ def get_opening_invoice_creation_dict(**args): { "qty": 1.0, "outstanding_amount": 300, - "party": "_Test {0}".format(party), + "party": args.get("party_1") or "_Test {0}".format(party), "item_name": "Opening Item", "due_date": "2016-09-10", "posting_date": "2016-09-05", @@ -101,7 +106,7 @@ def get_opening_invoice_creation_dict(**args): { "qty": 2.0, "outstanding_amount": 250, - "party": "_Test {0} 1".format(party), + "party": args.get("party_2") or "_Test {0} 1".format(party), "item_name": "Opening Item", "due_date": "2016-09-10", "posting_date": "2016-09-05", @@ -123,4 +128,19 @@ def make_company(): company.default_currency = "INR" company.country = "India" company.insert() - return company \ No newline at end of file + return company + +def make_customer(customer=None): + customer_name = customer or "Opening Customer" + customer = frappe.get_doc({ + "doctype": "Customer", + "customer_name": customer_name, + "customer_group": "All Customer Groups", + "customer_type": "Company", + "territory": "All Territories" + }) + if not frappe.db.exists("Customer", customer_name): + customer.insert(ignore_permissions=True) + return customer.name + else: + return frappe.db.exists("Customer", customer_name) \ No newline at end of file From 931f2e73a7a741a695072fe598f284d4b22b435b Mon Sep 17 00:00:00 2001 From: Anuja P Date: Fri, 4 Dec 2020 14:57:23 +0530 Subject: [PATCH 224/283] fix: sider changes --- erpnext/support/doctype/issue/issue.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 521e671b0d..086755be51 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -155,7 +155,7 @@ frappe.ui.form.on("Issue", { timeline_refresh: function(frm) { // create button for "Help Article" - if(frappe.model.can_create("Help Article")) { + if (frappe.model.can_create("Help Article")) { // Removing Help Article button if exists to avoid multiple occurance frm.timeline.wrapper.find('.comment-header .asset-details .btn-add-to-kb').remove(); $('
` ); this.$component = this.wrapper.find('.items-selector'); + this.$items_container = this.$component.find('.items-container'); } async load_items_data() { @@ -65,7 +68,6 @@ erpnext.PointOfSale.ItemSelector = class { render_item_list(items) { - this.$items_container = this.$component.find('.items-container'); this.$items_container.html(''); items.forEach(item => { @@ -75,11 +77,12 @@ erpnext.PointOfSale.ItemSelector = class { } get_item_html(item) { + const me = this; const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item; const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange"; function get_item_image_html() { - if (item_image) { + if (!me.hide_images && item_image) { return `
${frappe.get_abbr(item.item_name)}
` @@ -203,6 +206,7 @@ erpnext.PointOfSale.ItemSelector = class { ignore_inputs: true, page: cur_page.page.page }); + // for selecting the last filtered item on search frappe.ui.keys.on("enter", () => { const selector_is_visible = this.$component.is(':visible'); @@ -235,6 +239,7 @@ erpnext.PointOfSale.ItemSelector = class { const items = this.search_index[search_term]; this.items = items; this.render_item_list(items); + this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); return; } } @@ -247,8 +252,13 @@ erpnext.PointOfSale.ItemSelector = class { } this.items = items; this.render_item_list(items); + this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); }); } + + add_filtered_item_to_cart() { + this.$items_container.find(".item-wrapper").click(); + } resize_selector(minimize) { minimize ? From ce1ca282962d1bafeb4c3c64853249c3e7ec96cd Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Thu, 10 Dec 2020 17:48:05 +0530 Subject: [PATCH 253/283] fix: linting Co-authored-by: Marica --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b15c92c659..d4479b3b88 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -408,7 +408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ show_description(row_to_modify.idx, row_to_modify.item_code); - this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1; frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, { item_code: data.item_code, qty: (row_to_modify.qty || 0) + 1 From 8abe7b91fe90003d28ae74fb50c4303acf09258a Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Thu, 10 Dec 2020 17:48:22 +0530 Subject: [PATCH 254/283] fix: linting Co-authored-by: Marica --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d4479b3b88..3bc20f8733 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -492,7 +492,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ d.item_code = ""; } - this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1: 1; + this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1; this.item_code(doc, cdt, cdn); }, From c9209237daa91e09832927c11ec223abbe6a99bb Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 10 Dec 2020 22:45:27 +0530 Subject: [PATCH 255/283] fix: Progress bar style --- erpnext/education/doctype/fee_schedule/fee_schedule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js index 75dd4469e8..0b4c2cdfc7 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js @@ -43,7 +43,7 @@ frappe.ui.form.on('Fee Schedule', { frm.reload_doc(); } if (data.progress) { - let progress_bar = $(cur_frm.dashboard.progress_area).find('.progress-bar'); + let progress_bar = $(cur_frm.dashboard.progress_area.body).find('.progress-bar'); if (progress_bar) { $(progress_bar).removeClass('progress-bar-danger').addClass('progress-bar-success progress-bar-striped'); $(progress_bar).css('width', data.progress+'%'); From f8d6726990007dd4f21160707d11ab6ddc050c8d Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Fri, 11 Dec 2020 13:28:23 +0530 Subject: [PATCH 256/283] fix(acc recv report): columns mismatch (#24109) Co-authored-by: Rucha Mahabal --- .../accounts_receivable.html | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index bb0d0a132a..79a6aabd98 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -42,11 +42,13 @@ {% if(filters.show_future_payments) { %} {% var balance_row = data.slice(-1).pop(); - var range1 = report.columns[11].label; - var range2 = report.columns[12].label; - var range3 = report.columns[13].label; - var range4 = report.columns[14].label; - var range5 = report.columns[15].label; + var start = filters.based_on_payment_terms ? 13 : 11; + var range1 = report.columns[start].label; + var range2 = report.columns[start+1].label; + var range3 = report.columns[start+2].label; + var range4 = report.columns[start+3].label; + var range5 = report.columns[start+4].label; + var range6 = report.columns[start+5].label; %} {% if(balance_row) { %} @@ -70,20 +72,34 @@ + - - - - - + + + + + + + @@ -91,6 +107,7 @@ + @@ -101,6 +118,7 @@ + @@ -218,15 +236,15 @@ + {%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %} {% if(!filters.show_future_payments) { %} - + {%= format_currency(data[i]["paid"], data[i]["currency"]) %} + {% } %} + {%= format_currency(data[i]["outstanding"], data[i]["currency"]) %} {% if(filters.show_future_payments) { %} {% if(report.report_name === "Accounts Receivable") { %} @@ -234,8 +252,8 @@ {%= data[i]["po_no"] %} {% } %} - - + + {% } %} {% } %} {% } else { %} @@ -256,10 +274,10 @@ {% } else { %} {% } %} - - - - + + + + {% } %} {% } %} From 3eea3c6c954ad9a3e774aca4afd41f219a9bc57a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Nov 2020 20:17:52 +0530 Subject: [PATCH 257/283] fix: Table 'tabStock Entry Detail' is specified twice --- erpnext/controllers/status_updater.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2555edf06b..8c05134ae4 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -254,22 +254,26 @@ class StatusUpdater(Document): if not args.get("second_source_extra_cond"): args["second_source_extra_cond"] = "" - args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s) + args['second_source_condition'] = frappe.db.sql(""" select ifnull((select sum(%(second_source_field)s) from `tab%(second_source_dt)s` where `%(second_join_field)s`="%(detail_id)s" - and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args + and (`tab%(second_source_dt)s`.docstatus=1) + %(second_source_extra_cond)s), 0) """ % args)[0][0] if args['detail_id']: if not args.get("extra_cond"): args["extra_cond"] = "" - frappe.db.sql("""update `tab%(target_dt)s` - set %(target_field)s = ( + args["source_dt_value"] = frappe.db.sql(""" (select ifnull(sum(%(source_field)s), 0) from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s" and (docstatus=1 %(cond)s) %(extra_cond)s) - %(second_source_condition)s - ) - %(update_modified)s + """ % args)[0][0] or 0.0 + + if args['second_source_condition']: + args["source_dt_value"] += flt(args['second_source_condition']) + + frappe.db.sql("""update `tab%(target_dt)s` + set %(target_field)s = %(source_dt_value)s %(update_modified)s where name='%(detail_id)s'""" % args) def _update_percent_field_in_targets(self, args, update_modified=True): From f17ea2ccabc5e42b4f3e9b3fe0373670109f75ad Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 11 Dec 2020 21:30:39 +0530 Subject: [PATCH 258/283] fix: Accounting for internal transfer invoices within same company (#24021) * fix: Accounting for internal transfer invoices within same company * fix: warehouse fetching * fix: Linting issues * fix: GL entry fixes and validation for intercompany account * fix: Account naming changes and other fixes * fix: Add test for internal transfer * fix: Test Case * fix: Add description for fields * fix: Commonfied code * fix: Map warehouse and serial no --- .../purchase_invoice/purchase_invoice.js | 10 ++ .../purchase_invoice/purchase_invoice.json | 32 ++++- .../purchase_invoice/purchase_invoice.py | 94 ++++++++------ .../purchase_invoice/purchase_invoice_list.js | 16 +-- .../doctype/sales_invoice/sales_invoice.js | 10 ++ .../doctype/sales_invoice/sales_invoice.json | 25 +++- .../doctype/sales_invoice/sales_invoice.py | 85 +++++++++---- .../sales_invoice/sales_invoice_list.js | 4 +- .../sales_invoice/test_sales_invoice.py | 115 +++++++++++++++++- erpnext/buying/doctype/supplier/supplier.py | 6 + erpnext/controllers/accounts_controller.py | 34 ++++++ erpnext/controllers/buying_controller.py | 21 +++- erpnext/controllers/stock_controller.py | 14 ++- erpnext/controllers/taxes_and_totals.py | 14 ++- .../public/js/controllers/taxes_and_totals.js | 11 +- erpnext/selling/doctype/customer/customer.py | 8 +- erpnext/setup/doctype/company/company.js | 3 +- erpnext/setup/doctype/company/company.json | 29 ++--- 18 files changed, 415 insertions(+), 116 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 1d41d0fa2a..7830cfd370 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -15,6 +15,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ return (doc.qty<=doc.received_qty) ? "green" : "orange"; }); } + + this.frm.set_query("unrealized_profit_loss_account", function() { + return { + filters: { + company: doc.company, + is_group: 0, + root_type: "Liability", + } + }; + }); }, onload: function() { this._super(); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 2df77a84c7..c64ffd878c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -127,6 +126,7 @@ "write_off_cost_center", "advances_section", "allocate_advances_automatically", + "adjust_advance_taxes", "get_advances", "advances", "payment_schedule_section", @@ -152,9 +152,11 @@ "is_opening", "against_expense_account", "column_break_63", + "unrealized_profit_loss_account", "status", "inter_company_invoice_reference", "is_internal_supplier", + "represents_company", "remarks", "subscription_section", "from_date", @@ -1223,7 +1225,7 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", + "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1 }, { @@ -1330,13 +1332,37 @@ "fieldtype": "Link", "label": "Project", "options": "Project" + }, + { + "default": "0", + "description": "Taxes paid while advance payment will be adjusted against this invoice", + "fieldname": "adjust_advance_taxes", + "fieldtype": "Check", + "label": "Adjust Advance Taxes" + }, + { + "depends_on": "eval:doc.is_internal_supplier", + "description": "Unrealized Profit / Loss account for intra-company transfers", + "fieldname": "unrealized_profit_loss_account", + "fieldtype": "Link", + "label": "Unrealized Profit / Loss Account", + "options": "Account" + }, + { + "depends_on": "eval:doc.is_internal_supplier", + "description": "Company which internal supplier represents", + "fetch_from": "supplier.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-10-30 13:57:18.266978", + "modified": "2020-12-11 12:46:12.796378", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 8bd788890a..d94d261c6b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -206,8 +206,8 @@ class PurchaseInvoice(BuyingController): ["Purchase Receipt", "purchase_receipt", "pr_detail"] ]) - def validate_warehouse(self): - if self.update_stock: + def validate_warehouse(self, for_validate=True): + if self.update_stock and for_validate: for d in self.get('items'): if not d.warehouse: frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}"). @@ -233,7 +233,7 @@ class PurchaseInvoice(BuyingController): if self.update_stock: self.validate_item_code() - self.validate_warehouse() + self.validate_warehouse(for_validate) if auto_accounting_for_stock: warehouse_account = get_warehouse_account_map(self.company) @@ -449,6 +449,7 @@ class PurchaseInvoice(BuyingController): self.get_asset_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) + self.make_internal_transfer_gl_entries(gl_entries) gl_entries = make_regional_gl_entries(gl_entries, self) @@ -457,7 +458,6 @@ class PurchaseInvoice(BuyingController): self.make_payment_gl_entries(gl_entries) self.make_write_off_gl_entry(gl_entries) self.make_gle_for_rounding_adjustment(gl_entries) - return gl_entries def check_asset_cwip_enabled(self): @@ -474,31 +474,30 @@ class PurchaseInvoice(BuyingController): # because rounded_total had value even before introcution of posting GLE based on rounded total grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total - if grand_total: - # Didnot use base_grand_total to book rounding loss gle - grand_total_in_company_currency = flt(grand_total * self.conversion_rate, - self.precision("grand_total")) - gl_entries.append( - self.get_gl_dict({ - "account": self.credit_to, - "party_type": "Supplier", - "party": self.supplier, - "due_date": self.due_date, - "against": self.against_expense_account, - "credit": grand_total_in_company_currency, - "credit_in_account_currency": grand_total_in_company_currency \ - if self.party_account_currency==self.company_currency else grand_total, - "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, - "against_voucher_type": self.doctype, - "project": self.project, - "cost_center": self.cost_center - }, self.party_account_currency, item=self) - ) + if grand_total and not self.is_internal_transfer(): + # Didnot use base_grand_total to book rounding loss gle + grand_total_in_company_currency = flt(grand_total * self.conversion_rate, + self.precision("grand_total")) + gl_entries.append( + self.get_gl_dict({ + "account": self.credit_to, + "party_type": "Supplier", + "party": self.supplier, + "due_date": self.due_date, + "against": self.against_expense_account, + "credit": grand_total_in_company_currency, + "credit_in_account_currency": grand_total_in_company_currency \ + if self.party_account_currency==self.company_currency else grand_total, + "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, + "against_voucher_type": self.doctype, + "project": self.project, + "cost_center": self.cost_center + }, self.party_account_currency, item=self) + ) def make_item_gl_entries(self, gl_entries): # item gl entries stock_items = self.get_stock_items() - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") if self.update_stock and self.auto_accounting_for_stock: warehouse_account = get_warehouse_account_map(self.company) @@ -526,7 +525,6 @@ class PurchaseInvoice(BuyingController): item, voucher_wise_stock_value, account_currency) if item.from_warehouse: - gl_entries.append(self.get_gl_dict({ "account": warehouse_account[item.warehouse]['account'], "against": warehouse_account[item.from_warehouse]["account"], @@ -546,16 +544,18 @@ class PurchaseInvoice(BuyingController): "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item)) - gl_entries.append( - self.get_gl_dict({ - "account": item.expense_account, - "against": self.supplier, - "debit": flt(item.base_net_amount, item.precision("base_net_amount")), - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "cost_center": item.cost_center, - "project": item.project - }, account_currency, item=item) - ) + # Do not book expense for transfer within same company transfer + if not self.is_internal_transfer(): + gl_entries.append( + self.get_gl_dict({ + "account": item.expense_account, + "against": self.supplier, + "debit": flt(item.base_net_amount, item.precision("base_net_amount")), + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "cost_center": item.cost_center, + "project": item.project + }, account_currency, item=item) + ) else: gl_entries.append( @@ -832,7 +832,8 @@ class PurchaseInvoice(BuyingController): }, account_currency, item=tax) ) # accumulate valuation tax - if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount): + if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount) \ + and not self.is_internal_transfer(): if self.auto_accounting_for_stock and not tax.cost_center: frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) valuation_tax.setdefault(tax.name, 0) @@ -876,8 +877,19 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "credit": valuation_tax[tax.name], "remarks": self.remarks or "Accounting Entry for Stock" - }, item=tax) - ) + }, item=tax)) + + def make_internal_transfer_gl_entries(self, gl_entries): + if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): + account_currency = get_account_currency(self.unrealized_profit_loss_account) + gl_entries.append( + self.get_gl_dict({ + "account": self.unrealized_profit_loss_account, + "against": self.supplier, + "credit": flt(self.total_taxes_and_charges), + "credit_in_account_currency": flt(self.base_total_taxes_and_charges), + "cost_center": self.cost_center + }, account_currency, item=self)) def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries @@ -1095,7 +1107,9 @@ class PurchaseInvoice(BuyingController): if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if outstanding_amount > 0 and due_date < nowdate: + if self.is_internal_transfer(): + self.status = 'Internal Transfer' + elif outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 86c2e408c0..8da7d6fe13 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -4,23 +4,25 @@ // render frappe.listview_settings['Purchase Invoice'] = { add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company", - "currency", "is_return", "release_date", "on_hold"], + "currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"], get_indicator: function(doc) { - if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') { + if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') { return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"]; - } else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { + } else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { if(cint(doc.on_hold) && !doc.release_date) { return [__("On Hold"), "darkgrey"]; - } else if(cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) { + } else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) { return [__("Temporarily on Hold"), "darkgrey"]; - } else if(frappe.datetime.get_diff(doc.due_date) < 0) { + } else if (frappe.datetime.get_diff(doc.due_date) < 0) { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; } else { return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"]; } - } else if(cint(doc.is_return)) { + } else if (cint(doc.is_return)) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; - } else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) { + } else if (doc.company == doc.represents_company && doc.is_internal_supplier) { + return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"]; + } else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 502e65ed8d..5efc32e11d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -580,6 +580,16 @@ frappe.ui.form.on('Sales Invoice', { }; }); + frm.set_query("unrealized_profit_loss_account", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + root_type: "Liability", + } + }; + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Sales Return', diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 17fbe2def9..6799fb986a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_auto_repeat": 1, "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -158,6 +157,7 @@ "more_information", "inter_company_invoice_reference", "is_internal_customer", + "represents_company", "customer_group", "campaign", "is_discounted", @@ -171,6 +171,7 @@ "c_form_applicable", "c_form_no", "column_break8", + "unrealized_profit_loss_account", "remarks", "sales_team_section_break", "sales_partner", @@ -1655,7 +1656,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled", + "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1, "read_only": 1 }, @@ -1950,13 +1951,31 @@ "fieldtype": "Data", "label": "Company Tax ID", "read_only": 1 + }, + { + "depends_on": "eval:doc.is_internal_customer", + "description": "Unrealized Profit / Loss account for intra-company transfers", + "fieldname": "unrealized_profit_loss_account", + "fieldtype": "Link", + "label": "Unrealized Profit / Loss Account", + "options": "Account" + }, + { + "depends_on": "eval:doc.is_internal_customer", + "description": "Company which internal customer represents", + "fetch_from": "customer.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-10-30 13:57:45.086303", + "modified": "2020-12-11 12:48:31.769958", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 81f425f868..ca6f22cc30 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -758,6 +758,7 @@ class SalesInvoice(SellingController): self.make_customer_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) + self.make_internal_transfer_gl_entries(gl_entries) self.make_item_gl_entries(gl_entries) @@ -777,7 +778,7 @@ class SalesInvoice(SellingController): # Checked both rounding_adjustment and rounded_total # because rounded_total had value even before introcution of posting GLE based on rounded total grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total - if grand_total: + if grand_total and not self.is_internal_transfer(): # Didnot use base_grand_total to book rounding loss gle grand_total_in_company_currency = flt(grand_total * self.conversion_rate, self.precision("grand_total")) @@ -816,6 +817,18 @@ class SalesInvoice(SellingController): }, account_currency, item=tax) ) + def make_internal_transfer_gl_entries(self, gl_entries): + if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): + account_currency = get_account_currency(self.unrealized_profit_loss_account) + gl_entries.append( + self.get_gl_dict({ + "account": self.unrealized_profit_loss_account, + "against": self.customer, + "debit": flt(self.total_taxes_and_charges), + "debit_in_account_currency": flt(self.base_total_taxes_and_charges), + "cost_center": self.cost_center + }, account_currency, item=self)) + def make_item_gl_entries(self, gl_entries): # income account gl entries for item in self.get("items"): @@ -838,22 +851,24 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) else: - income_account = (item.income_account - if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) + # Do not book income for transfer within same company + if not self.is_internal_transfer(): + income_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) - account_currency = get_account_currency(income_account) - gl_entries.append( - self.get_gl_dict({ - "account": income_account, - "against": self.customer, - "credit": flt(item.base_net_amount, item.precision("base_net_amount")), - "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) - if account_currency==self.company_currency - else flt(item.net_amount, item.precision("net_amount"))), - "cost_center": item.cost_center, - "project": item.project or self.project - }, account_currency, item=item) - ) + account_currency = get_account_currency(income_account) + gl_entries.append( + self.get_gl_dict({ + "account": income_account, + "against": self.customer, + "credit": flt(item.base_net_amount, item.precision("base_net_amount")), + "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) + if account_currency==self.company_currency + else flt(item.net_amount, item.precision("net_amount"))), + "cost_center": item.cost_center, + "project": item.project or self.project + }, account_currency, item=item) + ) # expense account gl entries if cint(self.update_stock) and \ @@ -1265,7 +1280,9 @@ class SalesInvoice(SellingController): if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': + if self.is_internal_transfer(): + self.status = 'Internal Transfer' + elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': self.status = "Overdue and Discounted" elif outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" @@ -1530,9 +1547,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): if doctype in ["Sales Invoice", "Sales Order"]: source_doc = frappe.get_doc(doctype, source_name) target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order" + source_document_warehouse_field = 'target_warehouse' + target_document_warehouse_field = 'from_warehouse' else: source_doc = frappe.get_doc(doctype, source_name) target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order" + source_document_warehouse_field = 'from_warehouse' + target_document_warehouse_field = 'target_warehouse' validate_inter_company_transaction(source_doc, doctype) details = get_inter_company_details(source_doc, doctype) @@ -1559,6 +1580,26 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): if currency: target_doc.currency = currency + item_field_map = { + "doctype": target_doctype + " Item", + "field_no_map": [ + "income_account", + "expense_account", + "cost_center", + "warehouse" + ] + } + + if source_doc.get('update_stock'): + item_field_map.update({ + 'field_map': { + source_document_warehouse_field: target_document_warehouse_field, + 'batch_no': 'batch_no', + 'serial_no': 'serial_no' + } + }) + + doclist = get_mapped_doc(doctype, source_name, { doctype: { "doctype": target_doctype, @@ -1567,15 +1608,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): "taxes_and_charges" ] }, - doctype +" Item": { - "doctype": target_doctype + " Item", - "field_no_map": [ - "income_account", - "expense_account", - "cost_center", - "warehouse" - ] - } + doctype +" Item": item_field_map }, target_doc, set_missing_values) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index 05d49df711..41140d1938 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -14,8 +14,8 @@ frappe.listview_settings['Sales Invoice'] = { "Credit Note Issued": "darkgrey", "Unpaid and Discounted": "orange", "Overdue and Discounted": "red", - "Overdue": "red" - + "Overdue": "red", + "Internal Transfer": "darkgrey" }; return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; }, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 46e954d948..22a4f33654 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1573,7 +1573,7 @@ class TestSalesInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - + def test_sales_invoice_with_project_link(self): from erpnext.projects.doctype.project.test_project import make_project @@ -1607,9 +1607,9 @@ class TestSalesInvoice(unittest.TestCase): debit_in_account_currency, credit_in_account_currency from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s order by account asc""", sales_invoice.name, as_dict=1) - + self.assertTrue(gl_entries) - + for gle in gl_entries: self.assertEqual(expected_values[gle.account]["project"], gle.project) @@ -1781,6 +1781,60 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(target_doc.company, "_Test Company 1") self.assertEqual(target_doc.supplier, "_Test Internal Supplier") + def test_internal_transfer_gl_entry(self): + ## Create internal transfer account + account = create_account(account_name="Unrealized Profit", + parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory") + + frappe.db.set_value('Company', '_Test Company with perpetual inventory', + 'unrealized_profit_loss_account', account) + + customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory", + "_Test Company with perpetual inventory") + + create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory", + "_Test Company with perpetual inventory") + + si = create_sales_invoice( + company = "_Test Company with perpetual inventory", + customer = customer, + debit_to = "Debtors - TCP1", + warehouse = "Stores - TCP1", + income_account = "Sales - TCP1", + expense_account = "Cost of Goods Sold - TCP1", + cost_center = "Main - TCP1", + currency = "INR", + do_not_save = 1 + ) + + si.selling_price_list = "_Test Price List Rest of the World" + si.update_stock = 1 + si.items[0].target_warehouse = 'Work In Progress - TCP1' + add_taxes(si) + si.save() + si.submit() + + target_doc = make_inter_company_transaction("Sales Invoice", si.name) + target_doc.company = '_Test Company with perpetual inventory' + target_doc.items[0].warehouse = 'Finished Goods - TCP1' + add_taxes(target_doc) + target_doc.save() + target_doc.submit() + + si_gl_entries = [ + ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()], + ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()] + ] + + check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) + + pi_gl_entries = [ + ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()], + ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()] + ] + + check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) + def test_eway_bill_json(self): if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'): address = frappe.get_doc({ @@ -2039,4 +2093,57 @@ def get_taxes_and_charges(): "parentfield": "taxes", "rate": 2, "row_id": 1 - }] \ No newline at end of file + }] + +def create_internal_customer(customer_name, represents_company, allowed_to_interact_with): + if not frappe.db.exists("Customer", customer_name): + customer = frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": customer_name, + "customer_type": "Individual", + "doctype": "Customer", + "territory": "_Test Territory", + "is_internal_customer": 1, + "represents_company": represents_company + }) + + customer.append("companies", { + "company": allowed_to_interact_with + }) + + customer.insert() + customer_name = customer.name + else: + customer_name = frappe.db.get_value("Customer", customer_name) + + return customer_name + +def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with): + if not frappe.db.exists("Supplier", supplier_name): + supplier = frappe.get_doc({ + "supplier_group": "_Test Supplier Group", + "supplier_name": supplier_name, + "doctype": "Supplier", + "is_internal_supplier": 1, + "represents_company": represents_company + }) + + supplier.append("companies", { + "company": allowed_to_interact_with + }) + + supplier.insert() + supplier_name = supplier.name + else: + supplier_name = frappe.db.exists("Supplier", supplier_name) + + return supplier_name + +def add_taxes(doc): + doc.append('taxes', { + 'account_head': '_Test Account Excise Duty - TCP1', + "charge_type": "On Net Total", + "cost_center": "Main - TCP1", + "description": "Excise Duty", + "rate": 12 + }) \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index df143eefa0..0ee9d180d9 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -49,6 +49,12 @@ class Supplier(TransactionBase): msgprint(_("Series is mandatory"), raise_exception=1) validate_party_accounts(self) + self.validate_internal_supplier() + + def validate_internal_supplier(self): + if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"): + frappe.throw(_("Internal Supplier for company {0} already exists").format( + frappe.bold(self.represents_company))) def on_trash(self): delete_contact_and_address('Supplier', self.name) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 93a79ec934..32c5d3a3b1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -107,6 +107,8 @@ class AccountsController(TransactionBase): else: self.validate_deferred_start_and_end_date() + self.set_inter_company_account() + validate_regional(self) if self.doctype != 'Material Request': apply_pricing_rule_on_transaction(self) @@ -932,6 +934,38 @@ class AccountsController(TransactionBase): else: return frappe.db.get_single_value("Global Defaults", "disable_rounded_total") + def set_inter_company_account(self): + """ + Set intercompany account for inter warehouse transactions + This account will be used in case billing company and internal customer's + representation company is same + """ + + if self.is_internal_transfer() and not self.unrealized_profit_loss_account: + unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account') + + if not unrealized_profit_loss_account: + msg = _("Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}").format( + frappe.bold(self.company)) + frappe.throw(msg) + + self.unrealized_profit_loss_account = unrealized_profit_loss_account + + def is_internal_transfer(self): + """ + It will an internal transfer if its an internal customer and representation + company is same as billing company + """ + if self.doctype == 'Sales Invoice': + internal_party_field = 'is_internal_customer' + else: + internal_party_field = 'is_internal_supplier' + + if self.get(internal_party_field) and (self.represents_company == self.company): + return True + + return False + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 5fabf7017b..286c4f4451 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -42,6 +42,7 @@ class BuyingController(StockController): self.validate_items() self.set_qty_as_per_stock_uom() self.validate_stock_or_nonstock_items() + self.update_tax_category_for_internal_transfer() self.validate_warehouse() self.validate_from_warehouse() self.set_supplier_address() @@ -94,13 +95,23 @@ class BuyingController(StockController): def validate_stock_or_nonstock_items(self): if self.meta.get_field("taxes") and not self.get_stock_items() and not self.get_asset_items(): - tax_for_valuation = [d for d in self.get("taxes") + msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items') + self.update_tax_category(msg) + + def update_tax_category_for_internal_transfer(self): + if self.doctype == 'Purchase Invoice' and self.is_internal_transfer(): + msg = _('Tax Category has been changed to "Total" as its an internal purchase.') + self.update_tax_category(msg) + + def update_tax_category(self, msg): + tax_for_valuation = [d for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"]] - if tax_for_valuation: - for d in tax_for_valuation: - d.category = 'Total' - msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) + if tax_for_valuation: + for d in tax_for_valuation: + d.category = 'Total' + + msgprint(msg) def validate_asset_return(self): if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return: diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2f7b361b39..683d7f77b5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -77,7 +77,7 @@ class StockController(AccountsController): if sle_list: for sle in sle_list: if warehouse_account.get(sle.warehouse): - # from warehouse account/ target warehouse account + # from warehouse account self.check_expense_account(item_row) @@ -92,9 +92,16 @@ class StockController(AccountsController): sle = self.update_stock_ledger_entries(sle) + # expense account/ target_warehouse / source_warehouse + if item_row.get('target_warehouse'): + warehouse = item_row.get('target_warehouse') + expense_account = warehouse_account[warehouse]["account"] + else: + expense_account = item_row.expense_account + gl_list.append(self.get_gl_dict({ "account": warehouse_account[sle.warehouse]["account"], - "against": item_row.expense_account, + "against": expense_account, "cost_center": item_row.cost_center, "project": item_row.project or self.get('project'), "remarks": self.get("remarks") or "Accounting Entry for Stock", @@ -102,9 +109,8 @@ class StockController(AccountsController): "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", }, warehouse_account[sle.warehouse]["account_currency"], item=item_row)) - # expense account gl_list.append(self.get_gl_dict({ - "account": item_row.expense_account, + "account": expense_account, "against": warehouse_account[sle.warehouse]["account"], "cost_center": item_row.cost_center, "project": item_row.project or self.get('project'), diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ad58f137ee..8dd2e5bacb 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -519,6 +519,17 @@ class calculate_taxes_and_totals(object): if self.doc.docstatus == 0: self.calculate_outstanding_amount() + def is_internal_invoice(self): + """ + Checks if its an internal transfer invoice + and decides if to calculate any out standing amount or not + """ + + if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer(): + return True + + return False + def calculate_outstanding_amount(self): # NOTE: # write_off_amount is only for POS Invoice @@ -526,7 +537,8 @@ class calculate_taxes_and_totals(object): if self.doc.doctype == "Sales Invoice": self.calculate_paid_amount() - if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos'): return + if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \ + self.is_internal_invoice(): return self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"]) self._set_in_company_currency(self.doc, ['write_off_amount']) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 99f3995a66..22e75780b8 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -609,6 +609,15 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.calculate_outstanding_amount(update_paid_amount); }, + is_internal_invoice: function() { + if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) { + if (this.frm.doc.company === this.frm.doc.represents_company) { + return true; + } + } + return false; + }, + calculate_outstanding_amount: function(update_paid_amount) { // NOTE: // paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice @@ -617,7 +626,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.calculate_paid_amount(); } - if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return; + if (this.frm.doc.is_return || (this.frm.doc.docstatus > 0) || this.is_internal_invoice()) return; frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 0172d9c128..29214ee06d 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -58,6 +58,7 @@ class Customer(TransactionBase): self.set_loyalty_program() self.check_customer_group_change() self.validate_default_bank_account() + self.validate_internal_customer() # set loyalty program tier if frappe.db.exists('Customer', self.name): @@ -82,6 +83,11 @@ class Customer(TransactionBase): if not is_company_account: frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) + def validate_internal_customer(self): + if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"): + frappe.throw(_("Internal Customer for company {0} already exists").format( + frappe.bold(self.represents_company))) + def on_update(self): self.validate_name_with_customer_group() self.create_primary_contact() @@ -398,7 +404,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, # form a list of emails and names to show to the user credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users] if not credit_controller_users_formatted: - frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) + 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_formatted)) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index cbf67b4cd6..36033d9dae 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -274,7 +274,8 @@ erpnext.company.setup_queries = function(frm) { ["default_employee_advance_account", {"root_type": "Asset"}], ["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}], ["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}], - ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}] + ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], + ["unrealized_profit_loss_account", {"root_type": "Liability"}] ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 40938ea0a5..d49ae7ce8a 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -46,10 +46,9 @@ "round_off_account", "round_off_cost_center", "write_off_account", - "discount_allowed_account", - "discount_received_account", "exchange_gain_loss_account", "unrealized_exchange_gain_loss_account", + "unrealized_profit_loss_account", "column_break0", "allow_account_creation_against_child_company", "default_payable_account", @@ -261,14 +260,14 @@ { "fieldname": "create_chart_of_accounts_based_on", "fieldtype": "Select", - "label": "Create Chart of Accounts Based on", + "label": "Create Chart Of Accounts Based On", "options": "\nStandard Template\nExisting Company" }, { "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", "fieldname": "chart_of_accounts", "fieldtype": "Select", - "label": "Chart of Accounts Template", + "label": "Chart Of Accounts Template", "no_copy": 1 }, { @@ -345,18 +344,6 @@ "label": "Write Off Account", "options": "Account" }, - { - "fieldname": "discount_allowed_account", - "fieldtype": "Link", - "label": "Discount Allowed Account", - "options": "Account" - }, - { - "fieldname": "discount_received_account", - "fieldtype": "Link", - "label": "Discount Received Account", - "options": "Account" - }, { "fieldname": "exchange_gain_loss_account", "fieldtype": "Link", @@ -740,6 +727,12 @@ "fieldtype": "Link", "label": "Default In Transit Warehouse", "options": "Warehouse" + }, + { + "fieldname": "unrealized_profit_loss_account", + "fieldtype": "Link", + "label": "Unrealized Profit / Loss Account", + "options": "Account" } ], "icon": "fa fa-building", @@ -747,7 +740,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-08-06 00:38:08.311216", + "modified": "2020-12-03 12:27:27.085094", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -808,4 +801,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} +} \ No newline at end of file From 67dfe5db0d6241292aaad9e0f5a14f85536ea446 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 26 Nov 2020 12:55:27 +0530 Subject: [PATCH 259/283] fix: shipping chanrges not sync in erpnext from shopify --- .../connectors/shopify_connection.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index efbaa71924..f0a05ed192 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -260,6 +260,15 @@ 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: + if shipping_charge.get("price"): + 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"), From 5bfd6831c4bbe5a451ed0fc54ea0cf74d6fdbf38 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 6 Dec 2020 17:21:30 +0530 Subject: [PATCH 260/283] fix: delete Receive at Warehouse entry on cancellation of Send to Warehouse entry --- erpnext/stock/doctype/stock_entry/stock_entry.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e3159b95c3..ab4f347d3a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -120,6 +120,7 @@ class StockEntry(StockController): self.update_transferred_qty() self.update_quality_inspection() self.delete_auto_created_batches() + self.delete_linked_stock_entry() if self.purpose == 'Material Transfer' and self.add_to_transit: self.set_material_request_transfer_status('Not Started') @@ -152,6 +153,12 @@ class StockEntry(StockController): frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry") .format(self.job_card)) + def delete_linked_stock_entry(self): + if self.purpose == "Send to Warehouse": + for d in frappe.get_all("Stock Entry", filters={"docstatus": 0, + "outgoing_stock_entry": self.name, "purpose": "Receive at Warehouse"}): + frappe.delete_doc("Stock Entry", d.name) + def set_transfer_qty(self): for item in self.get("items"): if not flt(item.qty): From edb99d4e53ac590bd09760f5b7635b6a2e4cf14b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 24 Nov 2020 23:23:00 +0530 Subject: [PATCH 261/283] fix: incorrect stock quantity if 'Allow Multiple Material Consumption' has enabled --- .../doctype/work_order/test_work_order.py | 33 +++++++++++++++++++ .../doctype/work_order/work_order.js | 3 +- .../stock/doctype/stock_entry/stock_entry.js | 5 +++ .../stock/doctype/stock_entry/stock_entry.py | 30 ++++++++--------- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index e53927918e..2bf3fbf75e 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -491,6 +491,39 @@ class TestWorkOrder(unittest.TestCase): work_order1.save() self.assertEqual(work_order1.operations[0].time_in_mins, 40.0) + def test_partial_material_consumption(self): + frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1) + wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4) + + ste_cancel_list = [] + ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item", + target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0) + ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", + target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0) + + ste_cancel_list.extend([ste1, ste2]) + + s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4)) + s.submit() + ste_cancel_list.append(s) + + ste1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2)) + ste1.submit() + ste_cancel_list.append(ste1) + + print(wo_order.name) + ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2)) + self.assertEquals(ste3.fg_completed_qty, 2) + + expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4} + for row in ste3.items: + self.assertEquals(row.qty, expected_qty.get(row.item_code)) + + for ste_doc in ste_cancel_list: + ste_doc.cancel() + + frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0) + def get_scrap_item_details(bom_no): scrap_items = {} for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item` diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 9ce465ccaf..a6086fb88d 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -545,7 +545,8 @@ erpnext.work_order = { var tbl = frm.doc.required_items || []; var tbl_lenght = tbl.length; for (var i = 0, len = tbl_lenght; i < len; i++) { - if (flt(frm.doc.required_items[i].required_qty) > flt(frm.doc.required_items[i].consumed_qty)) { + let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; + if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { counter += 1; } } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 91217582ca..27fcbb7e2a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -841,6 +841,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ } }, + fg_completed_qty: function() { + this.get_items(); + }, + get_items: function() { var me = this; if(!this.frm.doc.fg_completed_qty || !this.frm.doc.bom_no) @@ -850,6 +854,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ // if work order / bom is mentioned, get items return this.frm.call({ doc: me.frm.doc, + freeze: true, method: "get_items", callback: function(r) { if(!r.exc) refresh_field("items"); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e3159b95c3..415f524365 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1033,26 +1033,22 @@ class StockEntry(StockController): wo = frappe.get_doc("Work Order", self.work_order) wo_items = frappe.get_all('Work Order Item', filters={'parent': self.work_order}, - fields=["item_code", "required_qty", "consumed_qty"] + fields=["item_code", "required_qty", "consumed_qty", "transferred_qty"] ) + work_order_qty = wo.material_transferred_for_manufacturing or wo.qty for item in wo_items: - qty = item.required_qty - item_account_details = get_item_defaults(item.item_code, self.company) # Take into account consumption if there are any. - if self.purpose == 'Manufacture': - req_qty_each = flt(item.required_qty / wo.qty) - if (flt(item.consumed_qty) != 0): - remaining_qty = flt(item.consumed_qty) - (flt(wo.produced_qty) * req_qty_each) - exhaust_qty = req_qty_each * wo.produced_qty - if remaining_qty > exhaust_qty : - if (remaining_qty/(req_qty_each * flt(self.fg_completed_qty))) >= 1: - qty =0 - else: - qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty - else: - qty = req_qty_each * flt(self.fg_completed_qty) + + wo_item_qty = item.transferred_qty or item.required_qty + + req_qty_each = ( + (flt(wo_item_qty) - flt(item.consumed_qty)) / + (flt(work_order_qty) - flt(wo.produced_qty)) + ) + + qty = req_qty_each * flt(self.fg_completed_qty) if qty > 0: self.add_to_stock_entry_detail({ @@ -1134,13 +1130,15 @@ class StockEntry(StockController): else: qty = req_qty_each * flt(self.fg_completed_qty) - elif backflushed_materials.get(item.item_code): for d in backflushed_materials.get(item.item_code): if d.get(item.warehouse): if (qty > req_qty): qty = (qty/trans_qty) * flt(self.fg_completed_qty) + if consumed_qty: + qty -= consumed_qty + if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')): qty = frappe.utils.ceil(qty) From ced3b13492b3ce1c95255d4edba54839ab3dbc78 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 12 Dec 2020 19:31:05 +0530 Subject: [PATCH 262/283] fix: Check for paid field --- erpnext/patches/v13_0/update_old_loans.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index caec53b3fd..561e967d6d 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,12 +23,14 @@ def execute(): frappe.reload_doc('accounts', 'doctype', 'journal_entry_account') updated_loan_types = [] + loans_to_close = [] # Update old loan status as closed - loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` - where paid = 0 and docstatus = 1""", as_dict=1) + if frappe.db.has_column('Repayment Schedule', 'paid'): + loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` + where paid = 0 and docstatus = 1""", as_dict=1) - loans_to_close = [d.parent for d in loans_list] + loans_to_close = [d.parent for d in loans_list] if loans_to_close: frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) From c838682188a71d2a71da68f132584414ed8bad83 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 27 Nov 2020 21:55:02 +0530 Subject: [PATCH 263/283] fix: Opening invoices in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 837929709e..ad3de5f398 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -151,6 +151,7 @@ class Gstr1Report(object): {select_columns} from `tab{doctype}` where docstatus = 1 {where_conditions} + and is_opening = 'No' order by posting_date desc """.format(select_columns=self.select_columns, doctype=self.doctype, where_conditions=conditions), self.filters, as_dict=1) From e64718b2ae7a7a92e8e542e1361437cd030e4015 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 15 Dec 2020 09:16:27 +0530 Subject: [PATCH 264/283] fix: selecting salary component (#24121) --- .../doctype/additional_salary/additional_salary.js | 8 -------- .../doctype/employee_incentive/employee_incentive.js | 4 ++-- .../payroll/doctype/salary_structure/salary_structure.js | 7 +++---- .../payroll/doctype/salary_structure/salary_structure.py | 2 +- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js index 0784de93eb..7737e6c886 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.js +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js @@ -12,14 +12,6 @@ frappe.ui.form.on('Additional Salary', { } }; }); - - if (!frm.doc.currency) return; - frm.set_query("salary_component", function() { - return { - query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", - filters: {currency: frm.doc.currency, company: frm.doc.company} - }; - }); }, employee: function(frm) { diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js index 85d1c54a22..182ce0f83a 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js @@ -11,11 +11,11 @@ frappe.ui.form.on('Employee Incentive', { }; }); - if (!frm.doc.currency) return; + if (!frm.doc.company) return; frm.set_query("salary_component", function() { return { query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", - filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + filters: {type: "earning", company: frm.doc.company} }; }); diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js index 7daae49c58..ba824c5d6f 100755 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -55,17 +55,17 @@ frappe.ui.form.on('Salary Structure', { }, set_earning_deduction_component: function(frm) { - if(!frm.doc.currency && !frm.doc.company) return; + if(!frm.doc.company) return; frm.set_query("salary_component", "earnings", function() { return { query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", - filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + filters: {type: "earning", company: frm.doc.company} }; }); frm.set_query("salary_component", "deductions", function() { return { query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", - filters: {type: "deduction", currency: frm.doc.currency, company: frm.doc.company} + filters: {type: "deduction", company: frm.doc.company} }; }); }, @@ -74,7 +74,6 @@ frappe.ui.form.on('Salary Structure', { currency: function(frm) { calculate_totals(frm.doc); frm.trigger("set_dynamic_labels") - frm.trigger('set_earning_deduction_component'); frm.refresh() }, diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index 877e41d93c..77914bb531 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -210,7 +210,7 @@ def get_employees(salary_structure): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters): - if len(filters) < 3: + if len(filters) < 2: return {} return frappe.db.sql(""" From 58e8e06ab7e1965fa4c37d0df9f986d58b095776 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 15 Dec 2020 09:17:17 +0530 Subject: [PATCH 265/283] fix: retention filters (#24123) * fix: retention filters * fix: slider --- erpnext/payroll/doctype/retention_bonus/retention_bonus.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js index 6fe8ccad46..f8bb40a9cb 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js @@ -4,9 +4,13 @@ frappe.ui.form.on('Retention Bonus', { setup: function(frm) { frm.set_query("employee", function() { + if (!frm.doc.company) { + frappe.msgprint(__("Please Select Company First")); + } return { filters: { - "status": "Active" + "status": "Active", + "company": frm.doc.company } }; }); From 89d14fdf6877f021057a23289a8a6c7e05fa061a Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 15 Dec 2020 09:31:30 +0530 Subject: [PATCH 266/283] fix: minor ui changes (#24125) * fix: minor ui changes * fix: slider --- .../employee_advance/employee_advance.js | 39 +++++++++++-------- .../employee_benefit_application.json | 7 +++- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 7056adf208..5037ceb489 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -18,13 +18,18 @@ frappe.ui.form.on('Employee Advance', { if (!frm.doc.employee) { frappe.msgprint(__("Please select employee first")); } - var company_currency = erpnext.get_currency(frm.doc.company); + let company_currency = erpnext.get_currency(frm.doc.company); + let currencies = [company_currency]; + if (frm.doc.currency && (frm.doc.currency != company_currency)) { + currencies.push(frm.doc.currency); + } + return { filters: { "root_type": "Asset", "is_group": 0, "company": frm.doc.company, - "account_currency": ["in", [frm.doc.currency, company_currency]], + "account_currency": ["in", currencies], } }; }); @@ -181,21 +186,23 @@ frappe.ui.form.on('Employee Advance', { }, currency: function(frm) { - var from_currency = frm.doc.currency; - var company_currency; - if (!frm.doc.company) { - company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); - } else { - company_currency = erpnext.get_currency(frm.doc.company); + if (frm.doc.currency) { + var from_currency = frm.doc.currency; + var company_currency; + if (!frm.doc.company) { + company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } else { + company_currency = erpnext.get_currency(frm.doc.company); + } + if (from_currency != company_currency) { + frm.events.set_exchange_rate(frm, from_currency, company_currency); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + } + frm.refresh_fields(); } - if (from_currency != company_currency) { - frm.events.set_exchange_rate(frm, from_currency, company_currency); - } else { - frm.set_value("exchange_rate", 1.0); - frm.set_df_property('exchange_rate', 'hidden', 1); - frm.set_df_property("exchange_rate", "description", "" ); - } - frm.refresh_fields(); }, set_exchange_rate: function(frm, from_currency, company_currency) { diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json index 9a5a463152..4c45580bf0 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -23,6 +23,7 @@ "employee_benefits", "totals", "total_amount", + "column_break", "pro_rata_dispensed_amount" ], "fields": [ @@ -139,11 +140,15 @@ "label": "Company", "options": "Company", "reqd": 1 + }, + { + "fieldname": "column_break", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-11-25 11:49:05.095101", + "modified": "2020-12-14 15:52:08.566418", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application", From 85213fa8cbcbadbfa97848433e5a15fda0220dd7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 15 Dec 2020 09:32:02 +0530 Subject: [PATCH 267/283] fix(Asset): set current asset value before calculating difference amount (#24119) --- .../doctype/asset_value_adjustment/asset_value_adjustment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index c2579ebf70..74ca62ffda 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -13,8 +13,8 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g class AssetValueAdjustment(Document): def validate(self): self.validate_date() - self.set_difference_amount() self.set_current_asset_value() + self.set_difference_amount() def on_submit(self): self.make_depreciation_entry() From f2206c27e75ad743ec73cc2332bed47917727689 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 15 Dec 2020 05:05:16 +0100 Subject: [PATCH 268/283] fix: allow other github links in same PR (#23995) --- .github/helper/documentation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py index b603ed5e53..9cc4663c39 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -21,8 +21,8 @@ def docs_link_exists(body): if word.startswith('http') and uri_validator(word): parsed_url = urlparse(word) if parsed_url.netloc == "github.com": - _, org, repo, _type, ref = parsed_url.path.split('/') - if org == "frappe" and repo in docs_repos: + parts = parsed_url.path.split('/') + if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos: return True From d050816e17af897fedf40fb8bee5c1e0a0d05dcb Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 15 Dec 2020 12:02:26 +0530 Subject: [PATCH 269/283] fix: overflow of customer selector --- erpnext/public/scss/point-of-sale.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 3e7d5dae82..c6270176ee 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -185,7 +185,7 @@ display: flex; flex-direction: column; padding: var(--padding-md) var(--padding-lg); - overflow: hidden; + overflow: visible; > .customer-field { display: flex; From c553453825d826da24516ffbde05fe2be1c3b938 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 15 Dec 2020 16:29:10 +0530 Subject: [PATCH 270/283] fix: user is not a field (#24129) --- erpnext/non_profit/doctype/member/member.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 44b975e9e9..25d6b53830 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -59,7 +59,7 @@ class Member(Document): frappe.msgprint(_("A customer is already linked to this Member")) cust = create_customer(frappe._dict({ 'fullname': self.member_name, - 'email': self.email_id or self.user, + 'email': self.email_id or self.email, 'phone': None })) @@ -177,4 +177,4 @@ def register_member(fullname, email, rzpay_plan_id, subscription_id, pan=None, m mobile=mobile )) - return member.name \ No newline at end of file + return member.name From 29778e2fba4b1f073fdfc048f784f755c57a1eeb Mon Sep 17 00:00:00 2001 From: Leela vadlamudi Date: Tue, 15 Dec 2020 21:23:17 +0530 Subject: [PATCH 271/283] feat: Voice Call Settings doctype added (#24126) --- erpnext/public/js/telephony.js | 2 +- .../doctype/voice_call_settings/__init__.py | 0 .../test_voice_call_settings.py | 10 ++ .../voice_call_settings.js | 8 ++ .../voice_call_settings.json | 124 ++++++++++++++++++ .../voice_call_settings.py | 10 ++ 6 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 erpnext/telephony/doctype/voice_call_settings/__init__.py create mode 100644 erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py create mode 100644 erpnext/telephony/doctype/voice_call_settings/voice_call_settings.js create mode 100644 erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json create mode 100644 erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js index bd7f890306..f9caadeed7 100644 --- a/erpnext/public/js/telephony.js +++ b/erpnext/public/js/telephony.js @@ -20,4 +20,4 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/telephony/doctype/voice_call_settings/__init__.py b/erpnext/telephony/doctype/voice_call_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.py new file mode 100644 index 0000000000..85d6adda09 --- /dev/null +++ b/erpnext/telephony/doctype/voice_call_settings/test_voice_call_settings.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 TestVoiceCallSettings(unittest.TestCase): + pass diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.js b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.js new file mode 100644 index 0000000000..4a61b612d0 --- /dev/null +++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.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('Voice Call Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json new file mode 100644 index 0000000000..25e55a22dc --- /dev/null +++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.json @@ -0,0 +1,124 @@ +{ + "actions": [], + "autoname": "field:user", + "creation": "2020-12-08 16:52:40.590146", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "call_receiving_device", + "column_break_3", + "greeting_message", + "agent_busy_message", + "agent_unavailable_message" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "permlevel": 1, + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "greeting_message", + "fieldtype": "Data", + "label": "Greeting Message" + }, + { + "fieldname": "agent_busy_message", + "fieldtype": "Data", + "label": "Agent Busy Message" + }, + { + "fieldname": "agent_unavailable_message", + "fieldtype": "Data", + "label": "Agent Unavailable Message" + }, + { + "default": "Computer", + "fieldname": "call_receiving_device", + "fieldtype": "Select", + "label": "Call Receiving Device", + "options": "Computer\nPhone" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-12-14 18:49:34.600194", + "modified_by": "Administrator", + "module": "Telephony", + "name": "Voice Call Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 2, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 2, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.py new file mode 100644 index 0000000000..ad3bbf1784 --- /dev/null +++ b/erpnext/telephony/doctype/voice_call_settings/voice_call_settings.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 VoiceCallSettings(Document): + pass From 34fc52850095625d5e173d7f166863454fca79ac Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Dec 2020 09:43:57 +0530 Subject: [PATCH 272/283] fix(minor): debounce taxes call --- erpnext/regional/india/taxes.js | 1 + erpnext/templates/includes/footer/footer_extension.html | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index b70b2ec48c..52b130c354 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -34,6 +34,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => { doctype: frm.doc.doctype, company: frm.doc.company }, + debounce: 2000, callback: function(r) { if(r.message) { frm.set_value('taxes_and_charges', r.message.taxes_and_charges); diff --git a/erpnext/templates/includes/footer/footer_extension.html b/erpnext/templates/includes/footer/footer_extension.html index 6171b61e38..c7f0d06dff 100644 --- a/erpnext/templates/includes/footer/footer_extension.html +++ b/erpnext/templates/includes/footer/footer_extension.html @@ -1,12 +1,12 @@ {% if not hide_footer_signup %}
    -
    -
    From 96a5e4effa54bb87c7700b0a060c2a119e02a0ac Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 16 Dec 2020 13:00:55 +0530 Subject: [PATCH 273/283] fix: Tax template update on customer address change --- erpnext/regional/india/taxes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index b70b2ec48c..87baece65d 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -12,6 +12,9 @@ erpnext.setup_auto_gst_taxation = (doctype) => { tax_category: function(frm) { frm.trigger('get_tax_template'); }, + customer_address: function(frm) { + frm.trigger('get_tax_template'); + }, get_tax_template: function(frm) { if (!frm.doc.company) return; From 70cfc4df15cbbea19c32a61d0f73edc075a3699e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 16 Dec 2020 20:55:47 +0530 Subject: [PATCH 274/283] fix: Remove dashboard page from home --- erpnext/setup/workspace/home/home.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json index c041bb32fb..13c1172fad 100644 --- a/erpnext/setup/workspace/home/home.json +++ b/erpnext/setup/workspace/home/home.json @@ -417,7 +417,7 @@ "type": "Link" } ], - "modified": "2020-12-01 13:38:38.131999", + "modified": "2020-12-16 10:24:52.088466", "modified_by": "Administrator", "module": "Setup", "name": "Home", @@ -445,11 +445,6 @@ "link_to": "Sales Invoice", "type": "DocType" }, - { - "label": "Dashboard", - "link_to": "dashboard", - "type": "Page" - }, { "label": "Leaderboard", "link_to": "leaderboard", From 2cd41bca065f14228a27f183068f936e0c9ea11f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Dec 2020 13:17:58 +0530 Subject: [PATCH 275/283] fix(routes): desk to app --- .../assessment_result_tool/assessment_result_tool.js | 2 +- .../course_scheduling_tool/course_scheduling_tool.js | 2 +- .../doctype/tally_migration/tally_migration.js | 4 ++-- erpnext/manufacturing/doctype/bom/bom.js | 2 +- erpnext/projects/doctype/task/task_list.js | 2 +- erpnext/public/js/call_popup/call_popup.js | 4 ++-- erpnext/public/js/communication.js | 2 +- erpnext/public/js/setup_wizard.js | 8 +++----- erpnext/selling/doctype/sales_order/sales_order.js | 2 +- erpnext/stock/dashboard/item_dashboard.js | 2 +- erpnext/stock/doctype/batch/batch.js | 2 +- erpnext/stock/doctype/item/item.js | 4 ++-- erpnext/stock/doctype/item_price/item_price.js | 2 +- erpnext/support/doctype/issue/issue.js | 5 +---- 14 files changed, 19 insertions(+), 24 deletions(-) diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js index e213309c5e..053f0c2f1b 100644 --- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js +++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js @@ -128,7 +128,7 @@ frappe.ui.form.on('Assessment Result Tool', { result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade); let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`); $(link_span).css("display", "block"); - $(link_span).find("a").attr("href", "/desk/Form/Assessment Result/"+assessment_result.name); + $(link_span).find("a").attr("href", "/app/assessment-result/"+assessment_result.name); } }); } diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js index f408dae7bd..4e2ccaa30c 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Course Scheduling Tool', {
  • ${course_schedules.map( - c => ` + c => `` ).join('')} diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index e8641114be..5482b9cc69 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -23,10 +23,10 @@ frappe.ui.form.on("Tally Migration", { frappe.msgprint({ message: __("An error has occurred during {0}. Check {1} for more details", [ - repl("%(tally_document)s", { + repl("%(tally_document)s", { tally_document: frm.docname }), - "Error Log" + "Error Log" ] ), title: __("Tally Migration Error"), diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 55f7a1b8a9..42662f6f8f 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -134,7 +134,7 @@ frappe.ui.form.on("BOM", { frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}', [ `variants`, - `${frm.doc.item}`, + `${frm.doc.item}`, ]), true); frm.$wrapper.find(".variants-intro").on("click", () => { diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js index 7c620317de..1b6c5fd9fe 100644 --- a/erpnext/projects/doctype/task/task_list.js +++ b/erpnext/projects/doctype/task/task_list.js @@ -26,7 +26,7 @@ frappe.listview_settings['Task'] = { }, gantt_custom_popup_html: function(ganttobj, task) { var html = `
    ${ganttobj.name}
    `; + href="/app/task/${ganttobj.id}""> ${ganttobj.name} `; if(task.project) html += `

    Project: ${task.project}

    `; html += `

    Progress: ${ganttobj.progress}

    `; diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 16e9cdb503..be1745e54f 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -85,7 +85,7 @@ class CallPopup {
    + href="/app/call-log/${this.call_log.name}"> ${__('View call log')} `, @@ -167,7 +167,7 @@ class CallPopup { const issue_field = this.dialog.get_field("last_issue"); issue_field.set_value(issue.subject); issue_field.$wrapper.append(` - + ${__('View all issues from {0}', [issue.customer])} `); diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index 38778e2ab0..7ce8b0913c 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -84,7 +84,7 @@ frappe.ui.form.on("Communication", { frm.reload_doc(); frappe.show_alert({ message: __("Opportunity {0} created", - ['' + r.message + '']), + ['' + r.message + '']), indicator: 'green' }); } diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 092f83903e..ef03b01698 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -127,11 +127,9 @@ erpnext.setup.slides_settings = [ options: "", fieldtype: 'Select' }, { fieldname: 'view_coa', label: __('View Chart of Accounts'), fieldtype: 'Button' }, - - { fieldtype: "Section Break", label: __('Financial Year') }, - { fieldname: 'fy_start_date', label: __('Start Date'), fieldtype: 'Date', reqd: 1 }, - { fieldtype: "Column Break" }, - { fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1 }, + { fieldname: 'fy_start_date', label: __('Financial Year Begins On'), fieldtype: 'Date', reqd: 1 }, + // end date should be hidden (auto calculated) + { fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1, hidden: 1 }, ], onload: function (slide) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index df812ad450..1cb71aeea1 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -436,7 +436,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( callback: function(r) { if(r.message) { frappe.msgprint(__('Material Request {0} submitted.', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); } d.hide(); me.frm.reload_doc(); diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index faa9b5df2f..f64d5931ae 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -198,7 +198,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb freeze: true, callback: function(r) { frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); dialog.hide(); callback(r); }, diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 7b2edff7e0..3b07e4e80c 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -102,7 +102,7 @@ frappe.ui.form.on('Batch', { }, callback: (r) => { frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); frm.refresh(); }, }); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 43e18d16fc..9e0941146a 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -85,7 +85,7 @@ frappe.ui.form.on("Item", { } if (frm.doc.variant_of) { frm.set_intro(__('This Item is a Variant of {0} (Template).', - [`${frm.doc.variant_of}`]), true); + [`${frm.doc.variant_of}`]), true); } if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) { @@ -649,7 +649,7 @@ $.extend(erpnext.item, { if (r.message) { var variant = r.message; frappe.msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes", - [repl('%(item)s', { + [repl('%(item)s', { item_encoded: encodeURIComponent(variant), item: variant })] diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 773fddcf96..017d248ffc 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -14,6 +14,6 @@ frappe.ui.form.on("Item Price", { frm.add_fetch("item_code", "stock_uom", "uom"); frm.set_df_property("bulk_import_help", "options", - '' + __("Import in Bulk") + ''); + '' + __("Import in Bulk") + ''); } }); diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 5b295d7254..158416ba79 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -189,10 +189,7 @@ frappe.ui.form.on("Issue", { subject: dialog.fields_dict.subject.value, communication_id: e.currentTarget.closest(".timeline-item").getAttribute("data-name") }, (r) => { - let url = window.location.href - let arr = url.split("/"); - let result = arr[0] + "//" + arr[2] - frappe.msgprint(`New issue created: ${r.message}`) + frappe.msgprint(`New issue created: ${r.message}`) frm.reload_doc(); dialog.hide(); }); From ec959bc3b9717ee724ad826fa71935ab1013680f Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 28 Oct 2020 11:02:02 +0530 Subject: [PATCH 276/283] fix: override field_map for job card gantt --- .../doctype/job_card/job_card_calendar.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js index cf07698ad6..f4877fdca0 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -8,7 +8,17 @@ frappe.views.calendar["Job Card"] = { "allDay": "allDay", "progress": "progress" }, - gantt: true, + gantt: { + field_map: { + "start": "started_time", + "end": "started_time", + "id": "name", + "title": "subject", + "color": "color", + "allDay": "allDay", + "progress": "progress" + } + }, filters: [ { "fieldtype": "Link", From 7d848b178509a92b7180b1f6773aff1151191163 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 14:51:42 +0530 Subject: [PATCH 277/283] refactor: Auto Repeat next schedule date function params (#23959) * refactor: Auto Repeat next schedule date function params * refactor: Auto Repeat next schedule date function params --- erpnext/selling/doctype/sales_order/sales_order.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 04d85e575c..accf59ebc4 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -418,8 +418,7 @@ class SalesOrder(SellingController): def on_recurring(self, reference_doc, auto_repeat_doc): def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date): - delivery_date = get_next_schedule_date(ref_doc_delivery_date, - auto_repeat_doc.frequency, auto_repeat_doc.start_date, cint(auto_repeat_doc.repeat_on_day)) + delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date) if delivery_date <= transaction_date: delivery_date_diff = frappe.utils.date_diff(ref_doc_delivery_date, red_doc_transaction_date) From 5f53b140e24c08ad475ec6102e5b3ac6affc73b5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 18:21:08 +0530 Subject: [PATCH 278/283] fix: Auto Repeat Import (#24157) --- erpnext/selling/doctype/sales_order/sales_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index accf59ebc4..9388e0927e 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -14,7 +14,6 @@ from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty from frappe.desk.notifications import clear_doctype_notifications from frappe.contacts.doctype.address.address import get_company_address from erpnext.controllers.selling_controller import SellingController -from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults From 7574a14d57c0432e0637ec677802a5c230b13ee5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Dec 2020 19:39:34 +0530 Subject: [PATCH 279/283] fix: Remove patch for setting next date in Subscription (#24158) --- erpnext/patches.txt | 1 - .../v9_0/fix_subscription_next_date.py | 48 ------------------- 2 files changed, 49 deletions(-) delete mode 100644 erpnext/patches/v9_0/fix_subscription_next_date.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e80bd6423e..a597b49ca5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -450,7 +450,6 @@ erpnext.patches.v8_9.set_member_party_type erpnext.patches.v9_0.add_user_to_child_table_in_pos_profile erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order erpnext.patches.v9_0.student_admission_childtable_migrate -erpnext.patches.v9_0.fix_subscription_next_date #2017-10-23 erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description erpnext.patches.v9_0.set_uoms_in_variant_field diff --git a/erpnext/patches/v9_0/fix_subscription_next_date.py b/erpnext/patches/v9_0/fix_subscription_next_date.py deleted file mode 100644 index 4595c8dc99..0000000000 --- a/erpnext/patches/v9_0/fix_subscription_next_date.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import getdate -from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date - -def execute(): - frappe.reload_doc('accounts', 'doctype', 'subscription') - fields = ["name", "reference_doctype", "reference_document", - "start_date", "frequency", "repeat_on_day"] - - for d in fields: - if not frappe.db.has_column('Subscription', d): - return - - doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice') - for data in frappe.get_all('Subscription', - fields = fields, - filters = {'reference_doctype': ('in', doctypes), 'docstatus': 1}): - - recurring_id = frappe.db.get_value(data.reference_doctype, data.reference_document, "recurring_id") - if recurring_id: - frappe.db.sql("update `tab{0}` set subscription=%s where recurring_id=%s" - .format(data.reference_doctype), (data.name, recurring_id)) - - date_field = 'transaction_date' - if data.reference_doctype in ['Sales Invoice', 'Purchase Invoice']: - date_field = 'posting_date' - - start_date = frappe.db.get_value(data.reference_doctype, data.reference_document, date_field) - - if start_date and getdate(start_date) != getdate(data.start_date): - last_ref_date = frappe.db.sql(""" - select {0} - from `tab{1}` - where subscription=%s and docstatus < 2 - order by creation desc - limit 1 - """.format(date_field, data.reference_doctype), data.name)[0][0] - - next_schedule_date = get_next_schedule_date(last_ref_date, data.frequency, data.repeat_on_day) - - frappe.db.set_value("Subscription", data.name, { - "start_date": start_date, - "next_schedule_date": next_schedule_date - }, None) \ No newline at end of file From 1725fbc59ae282fcb09dc4f4bbb0b56a468179eb Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 9 Dec 2020 17:57:29 +0100 Subject: [PATCH 280/283] feat: separate equity tree in CoA SKR04 --- ..._kontenplan_SKR04_with_account_number.json | 229 +++++++++++------- 1 file changed, 138 insertions(+), 91 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index 3fc109bfd6..849df18c6f 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -910,98 +910,8 @@ }, "is_group": 1 }, - "Passiva": { + "Passiva - Verbindlichkeiten": { "root_type": "Liability", - "A - Eigenkapital": { - "account_type": "Equity", - "is_group": 1, - "I - Gezeichnetes Kapital": { - "account_type": "Equity", - "is_group": 1, - "Gezeichnetes Kapital": { - "account_type": "Equity", - "account_number": "2900" - }, - "Ausstehende Einlagen auf das gezeichnete Kapital": { - "account_number": "2910", - "is_group": 1 - } - }, - "II - Kapitalr\u00fccklage": { - "account_type": "Equity", - "is_group": 1, - "Kapitalr\u00fccklage": { - "account_number": "2920" - } - }, - "III - Gewinnr\u00fccklagen": { - "account_type": "Equity", - "1 - gesetzliche R\u00fccklage": { - "account_type": "Equity", - "is_group": 1, - "Gesetzliche R\u00fccklage": { - "account_number": "2930" - } - }, - "2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": { - "account_type": "Equity", - "is_group": 1 - }, - "3 - satzungsm\u00e4\u00dfige R\u00fccklagen": { - "account_type": "Equity", - "is_group": 1, - "Satzungsm\u00e4\u00dfige R\u00fccklagen": { - "account_number": "2950" - } - }, - "4 - andere Gewinnr\u00fccklagen": { - "account_type": "Equity", - "is_group": 1, - "Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": { - "is_group": 1, - "Gewinnr\u00fccklagen (BilMoG)": { - "account_number": "2963" - }, - "Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": { - "account_number": "2964" - }, - "Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": { - "account_number": "2965" - }, - "Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": { - "account_number": "2966" - } - }, - "Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": { - "account_number": "2967" - }, - "Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": { - "account_number": "2968" - }, - "Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": { - "account_number": "2969" - } - }, - "is_group": 1 - }, - "IV - Gewinnvortrag/Verlustvortrag": { - "account_type": "Equity", - "is_group": 1, - "Gewinnvortrag vor Verwendung": { - "account_number": "2970" - }, - "Verlustvortrag vor Verwendung": { - "account_number": "2978" - } - }, - "V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": { - "account_type": "Equity", - "is_group": 1 - }, - "Einlagen stiller Gesellschafter": { - "account_number": "9295" - } - }, "B - R\u00fcckstellungen": { "is_group": 1, "1 - R\u00fcckstellungen f. Pensionen und \u00e4hnliche Verplicht.": { @@ -1618,6 +1528,143 @@ }, "is_group": 1 }, + "Passiva - Eigenkapital": { + "root_type": "Equity", + "A - Eigenkapital": { + "account_type": "Equity", + "is_group": 1, + "I - Gezeichnetes Kapital": { + "account_type": "Equity", + "is_group": 1, + "Gezeichnetes Kapital": { + "account_number": "2900", + "account_type": "Equity" + }, + "Gesch\u00e4ftsguthaben der verbleibenden Mitglieder": { + "account_number": "2901" + }, + "Gesch\u00e4ftsguthaben der ausscheidenden Mitglieder": { + "account_number": "2902" + }, + "Gesch\u00e4ftsguthaben aus gek\u00fcndigten Gesch\u00e4ftsanteilen": { + "account_number": "2903" + }, + "R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": { + "account_number": "2906" + }, + "Gegenkonto R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": { + "account_number": "2907" + }, + "Kapitalerh\u00f6hung aus Gesellschaftsmitteln": { + "account_number": "2908" + }, + "Ausstehende Einlagen auf das gezeichnete Kapital, nicht eingefordert": { + "account_number": "2910" + } + }, + "II - Kapitalr\u00fccklage": { + "account_type": "Equity", + "is_group": 1, + "Kapitalr\u00fccklage": { + "account_number": "2920" + }, + "Kapitalr\u00fccklage durch Ausgabe von Anteilen \u00fcber Nennbetrag": { + "account_number": "2925" + }, + "Kapitalr\u00fccklage durch Ausgabe von Schuldverschreibungen": { + "account_number": "2926" + }, + "Kapitalr\u00fccklage durch Zuzahlungen gegen Gew\u00e4hrung eines Vorzugs": { + "account_number": "2927" + }, + "Kapitalr\u00fccklage durch Zuzahlungen in das Eigenkapital": { + "account_number": "2928" + }, + "Nachschusskapital (Gegenkonto 1299)": { + "account_number": "2929" + } + }, + "III - Gewinnr\u00fccklagen": { + "account_type": "Equity", + "1 - gesetzliche R\u00fccklage": { + "account_type": "Equity", + "is_group": 1, + "Gesetzliche R\u00fccklage": { + "account_number": "2930" + } + }, + "2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": { + "account_type": "Equity", + "is_group": 1, + "R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": { + "account_number": "2935" + } + }, + "3 - satzungsm\u00e4\u00dfige R\u00fccklagen": { + "account_type": "Equity", + "is_group": 1, + "Satzungsm\u00e4\u00dfige R\u00fccklagen": { + "account_number": "2950" + } + }, + "4 - andere Gewinnr\u00fccklagen": { + "account_type": "Equity", + "is_group": 1, + "Andere Gewinnr\u00fccklagen": { + "account_number": "2960" + }, + "Andere Gewinnr\u00fccklagen aus dem Erwerb eigener Anteile": { + "account_number": "2961" + }, + "Eigenkapitalanteil von Wertaufholungen": { + "account_number": "2962" + }, + "Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": { + "is_group": 1, + "Gewinnr\u00fccklagen (BilMoG)": { + "account_number": "2963" + }, + "Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": { + "account_number": "2964" + }, + "Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": { + "account_number": "2965" + }, + "Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": { + "account_number": "2966" + } + }, + "Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": { + "account_number": "2967" + }, + "Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": { + "account_number": "2968" + }, + "Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": { + "account_number": "2969" + } + }, + "is_group": 1 + }, + "IV - Gewinnvortrag/Verlustvortrag": { + "account_type": "Equity", + "is_group": 1, + "Gewinnvortrag vor Verwendung": { + "account_number": "2970" + }, + "Verlustvortrag vor Verwendung": { + "account_number": "2978" + } + }, + "V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": { + "account_type": "Equity", + "is_group": 1 + }, + "Einlagen stiller Gesellschafter": { + "account_number": "9295" + } + } + }, "1 - Umsatzerl\u00f6se": { "root_type": "Income", "is_group": 1, From 64fa0e87293bac6878d38a5c04c6f4ee07c3c161 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 17 Dec 2020 14:29:52 +0530 Subject: [PATCH 281/283] fix: wrap assignees in a list --- erpnext/crm/doctype/appointment/appointment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py index 63efeb3cb6..2009ebf7cb 100644 --- a/erpnext/crm/doctype/appointment/appointment.py +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -126,7 +126,7 @@ class Appointment(Document): add_assignemnt({ 'doctype': self.doctype, 'name': self.name, - 'assign_to': existing_assignee + 'assign_to': [existing_assignee] }) return if self._assign: @@ -139,7 +139,7 @@ class Appointment(Document): add_assignemnt({ 'doctype': self.doctype, 'name': self.name, - 'assign_to': agent + 'assign_to': [agent] }) break From 46bc7ca69fafb1bd1345b53e56175e4e300e54c6 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 22 Dec 2020 12:32:13 +0530 Subject: [PATCH 282/283] feat: use call icon --- erpnext/public/js/telephony.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js index f9caadeed7..6cb1207e79 100644 --- a/erpnext/public/js/telephony.js +++ b/erpnext/public/js/telephony.js @@ -11,7 +11,7 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlData.extend( { .append(` - + ${frappe.utils.icon('call')} `) .find('.phone-btn') From ab1c2b763118cbcb7fdd6a19a9cd175ae47d0b54 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 23 Dec 2020 23:26:58 +0530 Subject: [PATCH 283/283] fix(minor): routing --- .../bom_stock_report/bom_stock_report.js | 4 +- erpnext/public/js/help_links.js | 244 +++++++----------- .../doctype/sales_order/sales_order.js | 2 +- erpnext/stock/doctype/shipment/shipment.js | 8 +- 4 files changed, 101 insertions(+), 157 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 8cd016461c..7beecaceed 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -27,9 +27,9 @@ frappe.query_reports["BOM Stock Report"] = { value = default_formatter(value, row, column, data); if (column.id == "item") { if (data["enough_parts_to_build"] > 0) { - value = `${data['item']}`; + value = `${data['item']}`; } else { - value = `${data['item']}`; + value = `${data['item']}`; } } return value diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index 66ff46405d..a436cac66b 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -2,13 +2,13 @@ frappe.provide('frappe.help.help_links'); const docsUrl = 'https://erpnext.com/docs/'; -frappe.help.help_links['Form/Rename Tool'] = [ +frappe.help.help_links['rename tool'] = [ { label: 'Bulk Rename', url: docsUrl + 'user/manual/en/setting-up/data/bulk-rename' }, ] //Setup -frappe.help.help_links['List/User'] = [ +frappe.help.help_links['user'] = [ { label: 'New User', url: docsUrl + 'user/manual/en/setting-up/users-and-permissions/adding-users' }, { label: 'Rename User', url: docsUrl + 'user/manual/en/setting-up/articles/rename-user' }, ] @@ -21,7 +21,7 @@ frappe.help.help_links['permission-manager'] = [ { label: 'Password', url: docsUrl + 'user/manual/en/setting-up/articles/change-password' }, ] -frappe.help.help_links['Form/System Settings'] = [ +frappe.help.help_links['system-settings'] = [ { label: 'Naming Series', url: docsUrl + 'user/manual/en/setting-up/settings/system-settings' }, ] @@ -30,64 +30,60 @@ frappe.help.help_links['data-import-tool'] = [ { label: 'Overwriting Data from Data Import Tool', url: docsUrl + 'user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool' }, ] -frappe.help.help_links['module_setup'] = [ - { label: 'Role Permissions Manager', url: docsUrl + 'user/manual/en/setting-up/users-and-permissions/role-based-permissions' }, -] - -frappe.help.help_links['Form/Naming Series'] = [ +frappe.help.help_links['naming-series'] = [ { label: 'Naming Series', url: docsUrl + 'user/manual/en/setting-up/settings/naming-series' }, { label: 'Setting the Current Value for Naming Series', url: docsUrl + 'user/manual/en/setting-up/articles/naming-series-current-value' }, ] -frappe.help.help_links['Form/Global Defaults'] = [ +frappe.help.help_links['global-defaults'] = [ { label: 'Global Settings', url: docsUrl + 'user/manual/en/setting-up/settings/global-defaults' }, ] -frappe.help.help_links['Form/Email Digest'] = [ +frappe.help.help_links['email-digest'] = [ { label: 'Email Digest', url: docsUrl + 'user/manual/en/setting-up/email/email-digest' }, ] -frappe.help.help_links['List/Print Heading'] = [ +frappe.help.help_links['print-heading'] = [ { label: 'Print Heading', url: docsUrl + 'user/manual/en/setting-up/print/print-headings' }, ] -frappe.help.help_links['List/Letter Head'] = [ +frappe.help.help_links['letter-head'] = [ { label: 'Letter Head', url: docsUrl + 'user/manual/en/setting-up/print/letter-head' }, ] -frappe.help.help_links['List/Address Template'] = [ +frappe.help.help_links['address-template'] = [ { label: 'Address Template', url: docsUrl + 'user/manual/en/setting-up/print/address-template' }, ] -frappe.help.help_links['List/Terms and Conditions'] = [ +frappe.help.help_links['terms-and-conditions'] = [ { label: 'Terms and Conditions', url: docsUrl + 'user/manual/en/setting-up/print/terms-and-conditions' }, ] -frappe.help.help_links['List/Cheque Print Template'] = [ +frappe.help.help_links['cheque-print-template'] = [ { label: 'Cheque Print Template', url: docsUrl + 'user/manual/en/setting-up/print/cheque-print-template' }, ] -frappe.help.help_links['List/Email Account'] = [ +frappe.help.help_links['email-account'] = [ { label: 'Email Account', url: docsUrl + 'user/manual/en/setting-up/email/email-account' }, ] -frappe.help.help_links['List/Notification'] = [ +frappe.help.help_links['notification'] = [ { label: 'Notification', url: docsUrl + 'user/manual/en/setting-up/email/notifications' }, ] -frappe.help.help_links['Form/Notification'] = [ +frappe.help.help_links['notification'] = [ { label: 'Notification', url: docsUrl + 'user/manual/en/setting-up/email/notifications' }, ] -frappe.help.help_links['List/Email Digest'] = [ +frappe.help.help_links['email-digest'] = [ { label: 'Email Digest', url: docsUrl + 'user/manual/en/setting-up/email/email-digest' }, ] -frappe.help.help_links['List/Auto Email Report'] = [ +frappe.help.help_links['auto-email-report'] = [ { label: 'Auto Email Reports', url: docsUrl + 'user/manual/en/setting-up/email/email-reports' }, ] -frappe.help.help_links['Form/Print Settings'] = [ +frappe.help.help_links['print-settings'] = [ { label: 'Print Settings', url: docsUrl + 'user/manual/en/setting-up/print/print-settings' }, ] @@ -95,66 +91,60 @@ frappe.help.help_links['print-format-builder'] = [ { label: 'Print Format Builder', url: docsUrl + 'user/manual/en/setting-up/print/print-settings' }, ] -frappe.help.help_links['List/Print Heading'] = [ +frappe.help.help_links['print-heading'] = [ { label: 'Print Heading', url: docsUrl + 'user/manual/en/setting-up/print/print-headings' }, ] //setup-integrations -frappe.help.help_links['Form/PayPal Settings'] = [ +frappe.help.help_links['paypal-settings'] = [ { label: 'PayPal Settings', url: docsUrl + 'user/manual/en/setting-up/integrations/paypal-integration' }, ] -frappe.help.help_links['Form/Razorpay Settings'] = [ +frappe.help.help_links['razorpay-settings'] = [ { label: 'Razorpay Settings', url: docsUrl + 'user/manual/en/setting-up/integrations/razorpay-integration' }, ] -frappe.help.help_links['Form/Dropbox Settings'] = [ +frappe.help.help_links['dropbox-settings'] = [ { label: 'Dropbox Settings', url: docsUrl + 'user/manual/en/setting-up/integrations/dropbox-backup' }, ] -frappe.help.help_links['Form/LDAP Settings'] = [ +frappe.help.help_links['ldap-settings'] = [ { label: 'LDAP Settings', url: docsUrl + 'user/manual/en/setting-up/integrations/ldap-integration' }, ] -frappe.help.help_links['Form/Stripe Settings'] = [ +frappe.help.help_links['stripe-settings'] = [ { label: 'Stripe Settings', url: docsUrl + 'user/manual/en/setting-up/integrations/stripe-integration' }, ] //Sales -frappe.help.help_links['Form/Quotation'] = [ +frappe.help.help_links['quotation'] = [ { label: 'Quotation', url: docsUrl + 'user/manual/en/selling/quotation' }, { label: 'Applying Discount', url: docsUrl + 'user/manual/en/selling/articles/applying-discount' }, { label: 'Sales Person', url: docsUrl + 'user/manual/en/selling/articles/sales-persons-in-the-sales-transactions' }, { label: 'Applying Margin', url: docsUrl + 'user/manual/en/selling/articles/adding-margin' }, ] -frappe.help.help_links['List/Customer'] = [ +frappe.help.help_links['customer'] = [ { label: 'Customer', url: docsUrl + 'user/manual/en/CRM/customer' }, { label: 'Credit Limit', url: docsUrl + 'user/manual/en/accounts/credit-limit' }, ] -frappe.help.help_links['Form/Customer'] = [ +frappe.help.help_links['customer'] = [ { label: 'Customer', url: docsUrl + 'user/manual/en/CRM/customer' }, { label: 'Credit Limit', url: docsUrl + 'user/manual/en/accounts/credit-limit' }, ] -frappe.help.help_links['List/Sales Taxes and Charges Template'] = [ +frappe.help.help_links['sales-taxes-and-charges-template'] = [ { label: 'Setting Up Taxes', url: docsUrl + 'user/manual/en/setting-up/setting-up-taxes' }, ] -frappe.help.help_links['Form/Sales Taxes and Charges Template'] = [ +frappe.help.help_links['sales-taxes-and-charges-template'] = [ { label: 'Setting Up Taxes', url: docsUrl + 'user/manual/en/setting-up/setting-up-taxes' }, ] -frappe.help.help_links['List/Sales Order'] = [ - { label: 'Sales Order', url: docsUrl + 'user/manual/en/selling/sales-order' }, - { label: 'Recurring Sales Order', url: docsUrl + 'user/manual/en/accounts/recurring-orders-and-invoices' }, - { label: 'Applying Discount', url: docsUrl + 'user/manual/en/selling/articles/applying-discount' }, -] - -frappe.help.help_links['Form/Sales Order'] = [ +frappe.help.help_links['sales-order'] = [ { label: 'Sales Order', url: docsUrl + 'user/manual/en/selling/sales-order' }, { label: 'Recurring Sales Order', url: docsUrl + 'user/manual/en/accounts/recurring-orders-and-invoices' }, { label: 'Applying Discount', url: docsUrl + 'user/manual/en/selling/articles/applying-discount' }, @@ -164,43 +154,34 @@ frappe.help.help_links['Form/Sales Order'] = [ { label: 'Applying Margin', url: docsUrl + 'user/manual/en/selling/articles/adding-margin' }, ] -frappe.help.help_links['Form/Product Bundle'] = [ +frappe.help.help_links['product-bundle'] = [ { label: 'Product Bundle', url: docsUrl + 'user/manual/en/selling/setup/product-bundle' }, ] -frappe.help.help_links['Form/Selling Settings'] = [ +frappe.help.help_links['selling-settings'] = [ { label: 'Selling Settings', url: docsUrl + 'user/manual/en/selling/setup/selling-settings' }, ] //Buying -frappe.help.help_links['List/Supplier'] = [ +frappe.help.help_links['supplier'] = [ { label: 'Supplier', url: docsUrl + 'user/manual/en/buying/supplier' }, ] -frappe.help.help_links['Form/Supplier'] = [ - { label: 'Supplier', url: docsUrl + 'user/manual/en/buying/supplier' }, -] - -frappe.help.help_links['Form/Request for Quotation'] = [ +frappe.help.help_links['request-for-quotation'] = [ { label: 'Request for Quotation', url: docsUrl + 'user/manual/en/buying/request-for-quotation' }, { label: 'RFQ Video', url: docsUrl + 'user/videos/learn/request-for-quotation.html' }, ] -frappe.help.help_links['Form/Supplier Quotation'] = [ +frappe.help.help_links['supplier-quotation'] = [ { label: 'Supplier Quotation', url: docsUrl + 'user/manual/en/buying/supplier-quotation' }, ] -frappe.help.help_links['Form/Buying Settings'] = [ +frappe.help.help_links['buying-settings'] = [ { label: 'Buying Settings', url: docsUrl + 'user/manual/en/buying/setup/buying-settings' }, ] -frappe.help.help_links['List/Purchase Order'] = [ - { label: 'Purchase Order', url: docsUrl + 'user/manual/en/buying/purchase-order' }, - { label: 'Recurring Purchase Order', url: docsUrl + 'user/manual/en/accounts/recurring-orders-and-invoices' }, -] - -frappe.help.help_links['Form/Purchase Order'] = [ +frappe.help.help_links['purchase-order'] = [ { label: 'Purchase Order', url: docsUrl + 'user/manual/en/buying/purchase-order' }, { label: 'Item UoM', url: docsUrl + 'user/manual/en/buying/articles/purchasing-in-different-unit' }, { label: 'Supplier Item Code', url: docsUrl + 'user/manual/en/buying/articles/maintaining-suppliers-part-no-in-item' }, @@ -208,44 +189,44 @@ frappe.help.help_links['Form/Purchase Order'] = [ { label: 'Subcontracting', url: docsUrl + 'user/manual/en/manufacturing/subcontracting' }, ] -frappe.help.help_links['List/Purchase Taxes and Charges Template'] = [ +frappe.help.help_links['purchase-taxes-and-charges-template'] = [ { label: 'Setting Up Taxes', url: docsUrl + 'user/manual/en/setting-up/setting-up-taxes' }, ] -frappe.help.help_links['List/POS Profile'] = [ +frappe.help.help_links['pos-profile'] = [ { label: 'POS Profile', url: docsUrl + 'user/manual/en/setting-up/pos-setting' }, ] -frappe.help.help_links['List/Price List'] = [ +frappe.help.help_links['price-list'] = [ { label: 'Price List', url: docsUrl + 'user/manual/en/setting-up/price-lists' }, ] -frappe.help.help_links['List/Authorization Rule'] = [ +frappe.help.help_links['authorization-rule'] = [ { label: 'Authorization Rule', url: docsUrl + 'user/manual/en/setting-up/authorization-rule' }, ] -frappe.help.help_links['Form/SMS Settings'] = [ +frappe.help.help_links['sms-settings'] = [ { label: 'SMS Settings', url: docsUrl + 'user/manual/en/setting-up/sms-setting' }, ] -frappe.help.help_links['List/Stock Reconciliation'] = [ +frappe.help.help_links['stock-reconciliation'] = [ { label: 'Stock Reconciliation', url: docsUrl + 'user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item' }, ] -frappe.help.help_links['Tree/Territory'] = [ +frappe.help.help_links['territory/view/tree'] = [ { label: 'Territory', url: docsUrl + 'user/manual/en/setting-up/territory' }, ] -frappe.help.help_links['Form/Dropbox Backup'] = [ +frappe.help.help_links['dropbox-backup'] = [ { label: 'Dropbox Backup', url: docsUrl + 'user/manual/en/setting-up/third-party-backups' }, { label: 'Setting Up Dropbox Backup', url: docsUrl + 'user/manual/en/setting-up/articles/setting-up-dropbox-backups' }, ] -frappe.help.help_links['List/Workflow'] = [ +frappe.help.help_links['workflow'] = [ { label: 'Workflow', url: docsUrl + 'user/manual/en/setting-up/workflows' }, ] -frappe.help.help_links['List/Company'] = [ +frappe.help.help_links['company'] = [ { label: 'Company', url: docsUrl + 'user/manual/en/setting-up/company-setup' }, { label: 'Managing Multiple Companies', url: docsUrl + 'user/manual/en/setting-up/articles/managing-multiple-companies' }, { label: 'Delete All Related Transactions for a Company', url: docsUrl + 'user/manual/en/setting-up/articles/delete-a-company-and-all-related-transactions' }, @@ -253,25 +234,25 @@ frappe.help.help_links['List/Company'] = [ //Accounts -frappe.help.help_links['modules/Accounts'] = [ +frappe.help.help_links['space/Accounts'] = [ { label: 'Introduction to Accounts', url: docsUrl + 'user/manual/en/accounts/' }, { label: 'Chart of Accounts', url: docsUrl + 'user/manual/en/accounts/chart-of-accounts.html' }, { label: 'Multi Currency Accounting', url: docsUrl + 'user/manual/en/accounts/multi-currency-accounting' }, ] -frappe.help.help_links['Tree/Account'] = [ +frappe.help.help_links['account/view/tree'] = [ { label: 'Chart of Accounts', url: docsUrl + 'user/manual/en/accounts/chart-of-accounts' }, { label: 'Managing Tree Mastes', url: docsUrl + 'user/manual/en/setting-up/articles/managing-tree-structure-masters' }, ] -frappe.help.help_links['Form/Sales Invoice'] = [ +frappe.help.help_links['sales-invoice'] = [ { label: 'Sales Invoice', url: docsUrl + 'user/manual/en/accounts/sales-invoice' }, { label: 'Accounts Opening Balance', url: docsUrl + 'user/manual/en/accounts/opening-accounts' }, { label: 'Sales Return', url: docsUrl + 'user/manual/en/stock/sales-return' }, { label: 'Recurring Sales Invoice', url: docsUrl + 'user/manual/en/accounts/recurring-orders-and-invoices' }, ] -frappe.help.help_links['List/Sales Invoice'] = [ +frappe.help.help_links['sales-invoice'] = [ { label: 'Sales Invoice', url: docsUrl + 'user/manual/en/accounts/sales-invoice' }, { label: 'Accounts Opening Balance', url: docsUrl + 'user/manual/en/accounts/opening-accounts' }, { label: 'Sales Return', url: docsUrl + 'user/manual/en/stock/sales-return' }, @@ -282,43 +263,43 @@ frappe.help.help_links['pos'] = [ { label: 'Point of Sale Invoice', url: docsUrl + 'user/manual/en/accounts/point-of-sale-pos-invoice' }, ] -frappe.help.help_links['List/POS Profile'] = [ +frappe.help.help_links['pos-profile'] = [ { label: 'Point of Sale Profile', url: docsUrl + 'user/manual/en/setting-up/pos-setting' }, ] -frappe.help.help_links['List/Purchase Invoice'] = [ +frappe.help.help_links['purchase-invoice'] = [ { label: 'Purchase Invoice', url: docsUrl + 'user/manual/en/accounts/purchase-invoice' }, { label: 'Accounts Opening Balance', url: docsUrl + 'user/manual/en/accounts/opening-accounts' }, { label: 'Recurring Purchase Invoice', url: docsUrl + 'user/manual/en/accounts/recurring-orders-and-invoices' }, ] -frappe.help.help_links['List/Journal Entry'] = [ +frappe.help.help_links['journal-entry'] = [ { label: 'Journal Entry', url: docsUrl + 'user/manual/en/accounts/journal-entry' }, { label: 'Advance Payment Entry', url: docsUrl + 'user/manual/en/accounts/advance-payment-entry' }, { label: 'Accounts Opening Balance', url: docsUrl + 'user/manual/en/accounts/opening-accounts' }, ] -frappe.help.help_links['List/Payment Entry'] = [ +frappe.help.help_links['payment-entry'] = [ { label: 'Payment Entry', url: docsUrl + 'user/manual/en/accounts/payment-entry' }, ] -frappe.help.help_links['List/Payment Request'] = [ +frappe.help.help_links['payment-request'] = [ { label: 'Payment Request', url: docsUrl + 'user/manual/en/accounts/payment-request' }, ] -frappe.help.help_links['List/Asset'] = [ +frappe.help.help_links['asset'] = [ { label: 'Managing Fixed Assets', url: docsUrl + 'user/manual/en/accounts/managing-fixed-assets' }, ] -frappe.help.help_links['List/Asset Category'] = [ +frappe.help.help_links['asset-category'] = [ { label: 'Asset Category', url: docsUrl + 'user/manual/en/accounts/managing-fixed-assets' }, ] -frappe.help.help_links['Tree/Cost Center'] = [ +frappe.help.help_links['cost-center/view/tree'] = [ { label: 'Budgeting', url: docsUrl + 'user/manual/en/accounts/budgeting' }, ] -frappe.help.help_links['List/Item'] = [ +frappe.help.help_links['item'] = [ { label: 'Item', url: docsUrl + 'user/manual/en/stock/item' }, { label: 'Item Price', url: docsUrl + 'user/manual/en/stock/item/item-price' }, { label: 'Barcode', url: docsUrl + 'user/manual/en/stock/articles/track-items-using-barcode' }, @@ -329,61 +310,42 @@ frappe.help.help_links['List/Item'] = [ { label: 'Item Valuation', url: docsUrl + 'user/manual/en/stock/item/item-valuation-fifo-and-moving-average' }, ] -frappe.help.help_links['Form/Item'] = [ - { label: 'Item', url: docsUrl + 'user/manual/en/stock/item' }, - { label: 'Item Price', url: docsUrl + 'user/manual/en/stock/item/item-price' }, - { label: 'Barcode', url: docsUrl + 'user/manual/en/stock/articles/track-items-using-barcode' }, - { label: 'Item Wise Taxation', url: docsUrl + 'user/manual/en/accounts/item-wise-taxation' }, - { label: 'Managing Fixed Assets', url: docsUrl + 'user/manual/en/accounts/managing-fixed-assets' }, - { label: 'Item Codification', url: docsUrl + 'user/manual/en/stock/item/item-codification' }, - { label: 'Item Variants', url: docsUrl + 'user/manual/en/stock/item/item-variants' }, - { label: 'Item Valuation', url: docsUrl + 'user/manual/en/stock/item/item-valuation-fifo-and-moving-average' }, -] - -frappe.help.help_links['List/Purchase Receipt'] = [ +frappe.help.help_links['purchase-receipt'] = [ { label: 'Purchase Receipt', url: docsUrl + 'user/manual/en/stock/purchase-receipt' }, { label: 'Barcode', url: docsUrl + 'user/manual/en/stock/articles/track-items-using-barcode' }, ] -frappe.help.help_links['List/Delivery Note'] = [ +frappe.help.help_links['delivery-note'] = [ { label: 'Delivery Note', url: docsUrl + 'user/manual/en/stock/delivery-note' }, { label: 'Barcode', url: docsUrl + 'user/manual/en/stock/articles/track-items-using-barcode' }, { label: 'Sales Return', url: docsUrl + 'user/manual/en/stock/sales-return' }, ] -frappe.help.help_links['Form/Delivery Note'] = [ +frappe.help.help_links['delivery-note'] = [ { label: 'Delivery Note', url: docsUrl + 'user/manual/en/stock/delivery-note' }, { label: 'Sales Return', url: docsUrl + 'user/manual/en/stock/sales-return' }, { label: 'Barcode', url: docsUrl + 'user/manual/en/stock/articles/track-items-using-barcode' }, { label: 'Subcontracting', url: docsUrl + 'user/manual/en/manufacturing/subcontracting' }, ] -frappe.help.help_links['List/Installation Note'] = [ +frappe.help.help_links['installation-note'] = [ { label: 'Installation Note', url: docsUrl + 'user/manual/en/stock/installation-note' }, ] -frappe.help.help_links['Tree'] = [ - { label: 'Managing Tree Structure Masters', url: docsUrl + 'user/manual/en/setting-up/articles/managing-tree-structure-masters' }, -] -frappe.help.help_links['List/Budget'] = [ +frappe.help.help_links['budget'] = [ { label: 'Budgeting', url: docsUrl + 'user/manual/en/accounts/budgeting' }, ] //Stock -frappe.help.help_links['List/Material Request'] = [ +frappe.help.help_links['material-request'] = [ { label: 'Material Request', url: docsUrl + 'user/manual/en/stock/material-request' }, { label: 'Auto-creation of Material Request', url: docsUrl + 'user/manual/en/stock/articles/auto-creation-of-material-request' }, ] -frappe.help.help_links['Form/Material Request'] = [ - { label: 'Material Request', url: docsUrl + 'user/manual/en/stock/material-request' }, - { label: 'Auto-creation of Material Request', url: docsUrl + 'user/manual/en/stock/articles/auto-creation-of-material-request' }, -] - -frappe.help.help_links['Form/Stock Entry'] = [ +frappe.help.help_links['stock-entry'] = [ { label: 'Stock Entry', url: docsUrl + 'user/manual/en/stock/stock-entry' }, { label: 'Stock Entry Types', url: docsUrl + 'user/manual/en/stock/articles/stock-entry-purpose' }, { label: 'Repack Entry', url: docsUrl + 'user/manual/en/stock/articles/repack-entry' }, @@ -391,136 +353,114 @@ frappe.help.help_links['Form/Stock Entry'] = [ { label: 'Subcontracting', url: docsUrl + 'user/manual/en/manufacturing/subcontracting' }, ] -frappe.help.help_links['List/Stock Entry'] = [ - { label: 'Stock Entry', url: docsUrl + 'user/manual/en/stock/stock-entry' }, -] - -frappe.help.help_links['Tree/Warehouse'] = [ +frappe.help.help_links['warehouse/view/tree'] = [ { label: 'Warehouse', url: docsUrl + 'user/manual/en/stock/warehouse' }, ] -frappe.help.help_links['List/Serial No'] = [ +frappe.help.help_links['serial-no'] = [ { label: 'Serial No', url: docsUrl + 'user/manual/en/stock/serial-no' }, ] -frappe.help.help_links['Form/Serial No'] = [ - { label: 'Serial No', url: docsUrl + 'user/manual/en/stock/serial-no' }, -] - -frappe.help.help_links['Form/Batch'] = [ +frappe.help.help_links['batch'] = [ { label: 'Batch', url: docsUrl + 'user/manual/en/stock/batch' }, ] -frappe.help.help_links['Form/Packing Slip'] = [ +frappe.help.help_links['packing-slip'] = [ { label: 'Packing Slip', url: docsUrl + 'user/manual/en/stock/tools/packing-slip' }, ] -frappe.help.help_links['Form/Quality Inspection'] = [ +frappe.help.help_links['quality-inspection'] = [ { label: 'Quality Inspection', url: docsUrl + 'user/manual/en/stock/tools/quality-inspection' }, ] -frappe.help.help_links['Form/Landed Cost Voucher'] = [ +frappe.help.help_links['landed-cost-voucher'] = [ { label: 'Landed Cost Voucher', url: docsUrl + 'user/manual/en/stock/tools/landed-cost-voucher' }, ] -frappe.help.help_links['Tree/Item Group'] = [ +frappe.help.help_links['item-group/view/tree'] = [ { label: 'Item Group', url: docsUrl + 'user/manual/en/stock/setup/item-group' }, ] -frappe.help.help_links['Form/Item Attribute'] = [ +frappe.help.help_links['item-attribute'] = [ { label: 'Item Attribute', url: docsUrl + 'user/manual/en/stock/setup/item-attribute' }, ] -frappe.help.help_links['Form/UOM'] = [ +frappe.help.help_links['uom'] = [ { label: 'Fractions in UOM', url: docsUrl + 'user/manual/en/stock/articles/managing-fractions-in-uom' }, ] -frappe.help.help_links['Form/Stock Reconciliation'] = [ +frappe.help.help_links['stock-reconciliation'] = [ { label: 'Opening Stock Entry', url: docsUrl + 'user/manual/en/stock/opening-stock' }, ] //CRM -frappe.help.help_links['Form/Lead'] = [ +frappe.help.help_links['lead'] = [ { label: 'Lead', url: docsUrl + 'user/manual/en/CRM/lead' }, ] -frappe.help.help_links['Form/Opportunity'] = [ +frappe.help.help_links['opportunity'] = [ { label: 'Opportunity', url: docsUrl + 'user/manual/en/CRM/opportunity' }, ] -frappe.help.help_links['Form/Address'] = [ +frappe.help.help_links['address'] = [ { label: 'Address', url: docsUrl + 'user/manual/en/CRM/address' }, ] -frappe.help.help_links['Form/Contact'] = [ +frappe.help.help_links['contact'] = [ { label: 'Contact', url: docsUrl + 'user/manual/en/CRM/contact' }, ] -frappe.help.help_links['Form/Newsletter'] = [ +frappe.help.help_links['newsletter'] = [ { label: 'Newsletter', url: docsUrl + 'user/manual/en/CRM/newsletter' }, ] -frappe.help.help_links['Form/Campaign'] = [ +frappe.help.help_links['campaign'] = [ { label: 'Campaign', url: docsUrl + 'user/manual/en/CRM/setup/campaign' }, ] -frappe.help.help_links['Tree/Sales Person'] = [ +frappe.help.help_links['sales-person/view/tree'] = [ { label: 'Sales Person', url: docsUrl + 'user/manual/en/CRM/setup/sales-person' }, ] -frappe.help.help_links['Form/Sales Person'] = [ +frappe.help.help_links['sales-person'] = [ { label: 'Sales Person Target', url: docsUrl + 'user/manual/en/selling/setup/sales-person-target-allocation' }, ] -//Support - -frappe.help.help_links['List/Feedback Trigger'] = [ - { label: 'Feedback Trigger', url: docsUrl + 'user/manual/en/setting-up/feedback/setting-up-feedback' }, -] - -frappe.help.help_links['List/Feedback Request'] = [ - { label: 'Feedback Request', url: docsUrl + 'user/manual/en/setting-up/feedback/submit-feedback' }, -] - -frappe.help.help_links['List/Feedback Request'] = [ - { label: 'Feedback Request', url: docsUrl + 'user/manual/en/setting-up/feedback/submit-feedback' }, -] - //Manufacturing -frappe.help.help_links['Form/BOM'] = [ +frappe.help.help_links['bom'] = [ { label: 'Bill of Material', url: docsUrl + 'user/manual/en/manufacturing/bill-of-materials' }, { label: 'Nested BOM Structure', url: docsUrl + 'user/manual/en/manufacturing/articles/nested-bom-structure' }, ] -frappe.help.help_links['Form/Work Order'] = [ +frappe.help.help_links['work-order'] = [ { label: 'Work Order', url: docsUrl + 'user/manual/en/manufacturing/work-order' }, ] -frappe.help.help_links['Form/Workstation'] = [ +frappe.help.help_links['workstation'] = [ { label: 'Workstation', url: docsUrl + 'user/manual/en/manufacturing/workstation' }, ] -frappe.help.help_links['Form/Operation'] = [ +frappe.help.help_links['operation'] = [ { label: 'Operation', url: docsUrl + 'user/manual/en/manufacturing/operation' }, ] -frappe.help.help_links['Form/BOM Update Tool'] = [ +frappe.help.help_links['bom-update-tool'] = [ { label: 'BOM Update Tool', url: docsUrl + 'user/manual/en/manufacturing/tools/bom-update-tool' }, ] //Customize -frappe.help.help_links['Form/Customize Form'] = [ +frappe.help.help_links['customize-form'] = [ { label: 'Custom Field', url: docsUrl + 'user/manual/en/customize-erpnext/custom-field' }, { label: 'Customize Field', url: docsUrl + 'user/manual/en/customize-erpnext/customize-form' }, ] -frappe.help.help_links['Form/Custom Field'] = [ +frappe.help.help_links['custom-field'] = [ { label: 'Custom Field', url: docsUrl + 'user/manual/en/customize-erpnext/custom-field' }, ] -frappe.help.help_links['Form/Custom Field'] = [ +frappe.help.help_links['custom-field'] = [ { label: 'Custom Field', url: docsUrl + 'user/manual/en/customize-erpnext/custom-field' }, ] diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 1cb71aeea1..842566b71f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -327,7 +327,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if(r.message) { frappe.msgprint({ message: __('Work Orders Created: {0}', [r.message.map(function(d) { - return repl('%(name)s', {name:d}) + return repl('%(name)s', {name:d}) }).join(', ')]), indicator: 'green' }) diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index 5ccb7d2ff6..7af16af898 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -150,7 +150,9 @@ frappe.ui.form.on('Shipment', { frm.set_value('pickup_contact_name', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") + "
    " + __("Please set Email/Phone for the contact") + ` ${contact_name}`); + frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") + + "
    " + __("Please set Email/Phone for the contact") + + ` ${contact_name}`); } let contact_display = r.message.contact_display; if (r.message.contact_email) { @@ -242,7 +244,9 @@ frappe.ui.form.on('Shipment', { frm.set_value('pickup_company', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
    " + __("Please first set Last Name, Email and Phone for the user") + ` ${frappe.session.user}`); + frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
    " + + __("Please first set Last Name, Email and Phone for the user") + + ` ${frappe.session.user}`); } let contact_display = r.full_name; if (r.email) {
    {%= __(range3) %} {%= __(range4) %} {%= __(range5) %}{%= __(range6) %} {%= __("Total") %}
    {%= __("Total Outstanding") %}{%= format_number(balance_row["range1"], null, 2) %}{%= format_currency(balance_row["range2"]) %}{%= format_currency(balance_row["range3"]) %}{%= format_currency(balance_row["range4"]) %}{%= format_currency(balance_row["range5"]) %} + {%= format_number(balance_row["age"], null, 2) %} + + {%= format_currency(balance_row["range1"], data[data.length-1]["currency"]) %} + + {%= format_currency(balance_row["range2"], data[data.length-1]["currency"]) %} + + {%= format_currency(balance_row["range3"], data[data.length-1]["currency"]) %} + + {%= format_currency(balance_row["range4"], data[data.length-1]["currency"]) %} + + {%= format_currency(balance_row["range5"], data[data.length-1]["currency"]) %} + {%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %} -
    {%= __("Future Payments") %} {%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %} {%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
    {%= __("Total") %} - {%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %} - {%= format_currency(data[i]["paid"], data[0]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} {%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} - {%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}{%= data[i]["future_ref"] %}{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}{%= __("Total") %}{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}{%= format_currency(data[i]["paid"], data[0]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}{%= format_currency(data[i]["paid"], data[i]["currency"]) %}{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}
    ${__("Course")}${__("Date")}
    ${c.name}
    ${c.name} ${c.schedule_date}